cb7369476c045411edba75ea459da274171315bb
[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 #include "BLI_fileops.h"
48
49 #include "DNA_constraint_types.h"
50 #include "DNA_controller_types.h"
51 #include "DNA_scene_types.h"
52 #include "DNA_screen_types.h"
53 #include "DNA_space_types.h"
54 #include "DNA_text_types.h"
55 #include "DNA_userdef_types.h"
56 #include "DNA_object_types.h"
57
58 #include "BKE_depsgraph.h"
59 #include "BKE_global.h"
60 #include "BKE_library.h"
61 #include "BKE_main.h"
62 #include "BKE_text.h"
63
64
65 #ifdef WITH_PYTHON
66 #include "BPY_extern.h"
67 #endif
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= BLI_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= BLI_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         }
759         else if (txt_get_span(text->lines.first, text->curl)<txt_get_span(text->lines.first, text->sell)) {
760                 *linep= text->curl;
761                 *charp= text->curc;
762         }
763         else {
764                 *linep= text->sell;
765                 *charp= text->selc;             
766         }
767 }
768
769 /*****************************/
770 /* Cursor movement functions */
771 /*****************************/
772
773 int txt_utf8_offset_to_index(const char *str, int offset)
774 {
775         int index= 0, pos= 0;
776         while (pos != offset) {
777                 pos += BLI_str_utf8_size(str + pos);
778                 index++;
779         }
780         return index;
781 }
782
783 int txt_utf8_index_to_offset(const char *str, int index)
784 {
785         int offset= 0, pos= 0;
786         while (pos != index) {
787                 offset += BLI_str_utf8_size(str + offset);
788                 pos++;
789         }
790         return offset;
791 }
792
793 /* returns the real number of characters in string */
794 /* not the same as BLI_strlen_utf8, which returns length for wide characters */
795 static int txt_utf8_len(const char *src)
796 {
797         int len;
798
799         for (len=0; *src; len++) {
800                 src += BLI_str_utf8_size(src);
801         }
802
803         return len;
804 }
805
806 void txt_move_up(Text *text, short sel)
807 {
808         TextLine **linep;
809         int *charp;
810         /* int old; */ /* UNUSED */
811         
812         if (!text) return;
813         if (sel) txt_curs_sel(text, &linep, &charp);
814         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
815         if (!*linep) return;
816         /* old= *charp; */ /* UNUSED */
817
818         if ((*linep)->prev) {
819                 int index = txt_utf8_offset_to_index((*linep)->line, *charp);
820                 *linep= (*linep)->prev;
821                 if (index > txt_utf8_len((*linep)->line)) *charp= (*linep)->len;
822                 else *charp= txt_utf8_index_to_offset((*linep)->line, index);
823                 
824                 if (!undoing)
825                         txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP);
826         }
827         else {
828                 txt_move_bol(text, sel);
829         }
830
831         if (!sel) txt_pop_sel(text);
832 }
833
834 void txt_move_down(Text *text, short sel) 
835 {
836         TextLine **linep;
837         int *charp;
838         /* int old; */ /* UNUSED */
839         
840         if (!text) return;
841         if (sel) txt_curs_sel(text, &linep, &charp);
842         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
843         if (!*linep) return;
844         /* old= *charp; */ /* UNUSED */
845
846         if ((*linep)->next) {
847                 int index = txt_utf8_offset_to_index((*linep)->line, *charp);
848                 *linep= (*linep)->next;
849                 if (index > txt_utf8_len((*linep)->line)) *charp= (*linep)->len;
850                 else *charp= txt_utf8_index_to_offset((*linep)->line, index);
851                 
852                 if (!undoing)
853                         txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN);
854         }
855         else {
856                 txt_move_eol(text, sel);
857         }
858
859         if (!sel) txt_pop_sel(text);
860 }
861
862 void txt_move_left(Text *text, short sel) 
863 {
864         TextLine **linep;
865         int *charp, oundoing= undoing;
866         int tabsize= 0, i= 0;
867         
868         if (!text) return;
869         if (sel) txt_curs_sel(text, &linep, &charp);
870         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
871         if (!*linep) return;
872
873         undoing= 1;
874
875         if (*charp== 0) {
876                 if ((*linep)->prev) {
877                         txt_move_up(text, sel);
878                         *charp= (*linep)->len;
879                 }
880         }
881         else {
882                 // do nice left only if there are only spaces
883                 // TXT_TABSIZE hardcoded in DNA_text_types.h
884                 if (text->flags & TXT_TABSTOSPACES) {
885                         tabsize= (*charp < TXT_TABSIZE) ? *charp : TXT_TABSIZE;
886                         
887                         for (i=0; i<(*charp); i++)
888                                 if ((*linep)->line[i] != ' ') {
889                                         tabsize= 0;
890                                         break;
891                                 }
892                         
893                         // if in the middle of the space-tab
894                         if (tabsize && (*charp) % TXT_TABSIZE != 0)
895                                 tabsize= ((*charp) % TXT_TABSIZE);
896                 }
897                 
898                 if (tabsize)
899                         (*charp)-= tabsize;
900                 else {
901                         const char *prev= BLI_str_prev_char_utf8((*linep)->line + *charp);
902                         *charp= prev - (*linep)->line;
903                 }
904         }
905
906         undoing= oundoing;
907         if (!undoing) txt_undo_add_op(text, sel?UNDO_SLEFT:UNDO_CLEFT);
908         
909         if (!sel) txt_pop_sel(text);
910 }
911
912 void txt_move_right(Text *text, short sel) 
913 {
914         TextLine **linep;
915         int *charp, oundoing= undoing, do_tab= 0, i;
916         
917         if (!text) return;
918         if (sel) txt_curs_sel(text, &linep, &charp);
919         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
920         if (!*linep) return;
921
922         undoing= 1;
923
924         if (*charp== (*linep)->len) {
925                 if ((*linep)->next) {
926                         txt_move_down(text, sel);
927                         *charp= 0;
928                 }
929         } 
930         else {
931                 // do nice right only if there are only spaces
932                 // spaces hardcoded in DNA_text_types.h
933                 if (text->flags & TXT_TABSTOSPACES && (*linep)->line[*charp]== ' ') {
934                         do_tab= 1;
935                         for (i=0; i<*charp; i++)
936                                 if ((*linep)->line[i]!= ' ') {
937                                         do_tab= 0;
938                                         break;
939                                 }
940                 }
941                 
942                 if (do_tab) {
943                         int tabsize= (*charp) % TXT_TABSIZE + 1;
944                         for (i=*charp+1; (*linep)->line[i]==' ' && tabsize<TXT_TABSIZE; i++)
945                                 tabsize++;
946                         (*charp)= i;
947                 }
948                 else (*charp)+= BLI_str_utf8_size((*linep)->line + *charp);
949         }
950         
951         undoing= oundoing;
952         if (!undoing) txt_undo_add_op(text, sel?UNDO_SRIGHT:UNDO_CRIGHT);
953
954         if (!sel) txt_pop_sel(text);
955 }
956
957 void txt_jump_left(Text *text, short sel)
958 {
959         TextLine **linep, *oldl;
960         int *charp, oldc, oldflags, i;
961         unsigned char oldu;
962         int pos;
963
964         if (!text) return;
965         if (sel) txt_curs_sel(text, &linep, &charp);
966         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
967         if (!*linep) return;
968
969         oldflags = text->flags;
970         text->flags &= ~TXT_TABSTOSPACES;
971
972         oldl= *linep;
973         oldc= *charp;
974         oldu= undoing;
975         undoing= 1; /* Don't push individual moves to undo stack */
976
977         pos = *charp;
978         BLI_str_cursor_step_utf8((*linep)->line, (*linep)->len,
979                                  &pos, STRCUR_DIR_PREV,
980                                  STRCUR_JUMP_DELIM);
981         for (i = *charp; i > pos; i--) {
982                 txt_move_left(text, sel);
983         }
984
985         text->flags = oldflags;
986
987         undoing= oldu;
988         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);
989 }
990
991 void txt_jump_right(Text *text, short sel)
992 {
993         TextLine **linep, *oldl;
994         int *charp, oldc, oldflags, i;
995         unsigned char oldu;
996         int pos;
997
998         if (!text) return;
999         if (sel) txt_curs_sel(text, &linep, &charp);
1000         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
1001         if (!*linep) return;
1002
1003         oldflags = text->flags;
1004         text->flags &= ~TXT_TABSTOSPACES;
1005
1006         oldl= *linep;
1007         oldc= *charp;
1008         oldu= undoing;
1009         undoing= 1; /* Don't push individual moves to undo stack */
1010
1011         pos = *charp;
1012         BLI_str_cursor_step_utf8((*linep)->line, (*linep)->len,
1013                                  &pos, STRCUR_DIR_NEXT,
1014                                  STRCUR_JUMP_DELIM);
1015         for (i = *charp; i < pos; i++) {
1016                 txt_move_right(text, sel);
1017         }
1018
1019         text->flags = oldflags;
1020
1021         undoing= oldu;
1022         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);
1023 }
1024
1025 void txt_move_bol (Text *text, short sel) 
1026 {
1027         TextLine **linep;
1028         int *charp, old;
1029         
1030         if (!text) return;
1031         if (sel) txt_curs_sel(text, &linep, &charp);
1032         else txt_curs_cur(text, &linep, &charp);
1033         if (!*linep) return;
1034         old= *charp;
1035         
1036         *charp= 0;
1037
1038         if (!sel) txt_pop_sel(text);
1039         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);
1040 }
1041
1042 void txt_move_eol (Text *text, short sel) 
1043 {
1044         TextLine **linep;
1045         int *charp, old;
1046         
1047         if (!text) return;
1048         if (sel) txt_curs_sel(text, &linep, &charp);
1049         else txt_curs_cur(text, &linep, &charp);
1050         if (!*linep) return;
1051         old= *charp;
1052                 
1053         *charp= (*linep)->len;
1054
1055         if (!sel) txt_pop_sel(text);
1056         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);
1057 }
1058
1059 void txt_move_bof (Text *text, short sel)
1060 {
1061         TextLine **linep;
1062         int *charp, old;
1063         
1064         if (!text) return;
1065         if (sel) txt_curs_sel(text, &linep, &charp);
1066         else txt_curs_cur(text, &linep, &charp);
1067         if (!*linep) return;
1068         old= *charp;
1069
1070         *linep= text->lines.first;
1071         *charp= 0;
1072
1073         if (!sel) txt_pop_sel(text);
1074         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);
1075 }
1076
1077 void txt_move_eof (Text *text, short sel)
1078 {
1079         TextLine **linep;
1080         int *charp, old;
1081         
1082         if (!text) return;
1083         if (sel) txt_curs_sel(text, &linep, &charp);
1084         else txt_curs_cur(text, &linep, &charp);
1085         if (!*linep) return;
1086         old= *charp;
1087
1088         *linep= text->lines.last;
1089         *charp= (*linep)->len;
1090
1091         if (!sel) txt_pop_sel(text);
1092         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);    
1093 }
1094
1095 void txt_move_toline (Text *text, unsigned int line, short sel)
1096 {
1097         txt_move_to(text, line, 0, sel);
1098 }
1099
1100 /* Moves to a certain byte in a line, not a certain utf8-character! */
1101 void txt_move_to (Text *text, unsigned int line, unsigned int ch, short sel)
1102 {
1103         TextLine **linep, *oldl;
1104         int *charp, oldc;
1105         unsigned int i;
1106         
1107         if (!text) return;
1108         if (sel) txt_curs_sel(text, &linep, &charp);
1109         else txt_curs_cur(text, &linep, &charp);
1110         if (!*linep) return;
1111         oldc= *charp;
1112         oldl= *linep;
1113         
1114         *linep= text->lines.first;
1115         for (i=0; i<line; i++) {
1116                 if ((*linep)->next) *linep= (*linep)->next;
1117                 else break;
1118         }
1119         if (ch>(unsigned int)((*linep)->len))
1120                 ch= (unsigned int)((*linep)->len);
1121         *charp= ch;
1122         
1123         if (!sel) txt_pop_sel(text);
1124         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);
1125 }
1126
1127 /****************************/
1128 /* Text selection functions */
1129 /****************************/
1130
1131 static void txt_curs_swap (Text *text)
1132 {
1133         TextLine *tmpl;
1134         int tmpc;
1135                 
1136         tmpl= text->curl;
1137         text->curl= text->sell;
1138         text->sell= tmpl;
1139         
1140         tmpc= text->curc;
1141         text->curc= text->selc;
1142         text->selc= tmpc;
1143         
1144         if (!undoing) txt_undo_add_op(text, UNDO_SWAP);
1145 }
1146
1147 static void txt_pop_first (Text *text)
1148 {
1149                         
1150         if (txt_get_span(text->curl, text->sell)<0 ||
1151                 (text->curl==text->sell && text->curc>text->selc)) {    
1152                 txt_curs_swap(text);
1153         }
1154
1155         if (!undoing) txt_undo_add_toop(text, UNDO_STO,
1156                 txt_get_span(text->lines.first, text->sell), 
1157                 text->selc, 
1158                 txt_get_span(text->lines.first, text->curl), 
1159                 text->curc);            
1160         
1161         txt_pop_sel(text);
1162 }
1163
1164 static void txt_pop_last (Text *text)
1165 {
1166         if (txt_get_span(text->curl, text->sell)>0 ||
1167                 (text->curl==text->sell && text->curc<text->selc)) {
1168                 txt_curs_swap(text);
1169         }
1170
1171         if (!undoing) txt_undo_add_toop(text, UNDO_STO,
1172                 txt_get_span(text->lines.first, text->sell), 
1173                 text->selc, 
1174                 txt_get_span(text->lines.first, text->curl), 
1175                 text->curc);            
1176         
1177         txt_pop_sel(text);
1178 }
1179
1180 /* never used: CVS 1.19 */
1181 /*  static void txt_pop_selr (Text *text) */
1182
1183 void txt_pop_sel (Text *text)
1184 {
1185         text->sell= text->curl;
1186         text->selc= text->curc; 
1187 }
1188
1189 void txt_order_cursors(Text *text)
1190 {
1191         if (!text) return;
1192         if (!text->curl) return;
1193         if (!text->sell) return;
1194         
1195                 /* Flip so text->curl is before text->sell */
1196         if ((txt_get_span(text->curl, text->sell) < 0) ||
1197             (text->curl == text->sell && text->curc > text->selc))
1198         {
1199                 txt_curs_swap(text);
1200         }
1201 }
1202
1203 int txt_has_sel(Text *text)
1204 {
1205         return ((text->curl!=text->sell) || (text->curc!=text->selc));
1206 }
1207
1208 static void txt_delete_sel (Text *text)
1209 {
1210         TextLine *tmpl;
1211         TextMarker *mrk;
1212         char *buf;
1213         int move, lineno;
1214         
1215         if (!text) return;
1216         if (!text->curl) return;
1217         if (!text->sell) return;
1218
1219         if (!txt_has_sel(text)) return;
1220         
1221         txt_order_cursors(text);
1222
1223         if (!undoing) {
1224                 buf= txt_sel_to_buf(text);
1225                 txt_undo_add_block(text, UNDO_DBLOCK, buf);
1226                 MEM_freeN(buf);
1227         }
1228
1229         buf= MEM_mallocN(text->curc+(text->sell->len - text->selc)+1, "textline_string");
1230         
1231         if (text->curl != text->sell) {
1232                 txt_clear_marker_region(text, text->curl, text->curc, text->curl->len, 0, 0);
1233                 move= txt_get_span(text->curl, text->sell);
1234         }
1235         else {
1236                 mrk= txt_find_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
1237                 if (mrk && (mrk->start > text->curc || mrk->end < text->selc))
1238                         txt_clear_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
1239                 move= 0;
1240         }
1241
1242         mrk= txt_find_marker_region(text, text->sell, text->selc-1, text->sell->len, 0, 0);
1243         if (mrk) {
1244                 lineno= mrk->lineno;
1245                 do {
1246                         mrk->lineno -= move;
1247                         if (mrk->start > text->curc) mrk->start -= text->selc - text->curc;
1248                         mrk->end -= text->selc - text->curc;
1249                         mrk= mrk->next;
1250                 } while (mrk && mrk->lineno==lineno);
1251         }
1252
1253         strncpy(buf, text->curl->line, text->curc);
1254         strcpy(buf+text->curc, text->sell->line + text->selc);
1255         buf[text->curc+(text->sell->len - text->selc)]=0;
1256
1257         make_new_line(text->curl, buf);
1258         
1259         tmpl= text->sell;
1260         while (tmpl != text->curl) {
1261                 tmpl= tmpl->prev;
1262                 if (!tmpl) break;
1263                 
1264                 txt_delete_line(text, tmpl->next);
1265         }
1266         
1267         text->sell= text->curl;
1268         text->selc= text->curc;
1269 }
1270
1271 void txt_sel_all (Text *text)
1272 {
1273         if (!text) return;
1274
1275         text->curl= text->lines.first;
1276         text->curc= 0;
1277         
1278         text->sell= text->lines.last;
1279         text->selc= text->sell->len;
1280 }
1281
1282 void txt_sel_line (Text *text)
1283 {
1284         if (!text) return;
1285         if (!text->curl) return;
1286         
1287         text->curc= 0;
1288         text->sell= text->curl;
1289         text->selc= text->sell->len;
1290 }
1291
1292 /***************************/
1293 /* Cut and paste functions */
1294 /***************************/
1295
1296 char *txt_to_buf (Text *text)
1297 {
1298         int length;
1299         TextLine *tmp, *linef, *linel;
1300         int charf, charl;
1301         char *buf;
1302         
1303         if (!text) return NULL;
1304         if (!text->curl) return NULL;
1305         if (!text->sell) return NULL;
1306         if (!text->lines.first) return NULL;
1307
1308         linef= text->lines.first;
1309         charf= 0;
1310                 
1311         linel= text->lines.last;
1312         charl= linel->len;
1313
1314         if (linef == text->lines.last) {
1315                 length= charl-charf;
1316
1317                 buf= MEM_mallocN(length+2, "text buffer");
1318                 
1319                 BLI_strncpy(buf, linef->line + charf, length+1);
1320                 buf[length]=0;
1321         }
1322         else {
1323                 length= linef->len - charf;
1324                 length+= charl;
1325                 length+= 2; /* For the 2 '\n' */
1326                 
1327                 tmp= linef->next;
1328                 while (tmp && tmp!= linel) {
1329                         length+= tmp->len+1;
1330                         tmp= tmp->next;
1331                 }
1332                 
1333                 buf= MEM_mallocN(length+1, "cut buffer");
1334
1335                 strncpy(buf, linef->line + charf, linef->len-charf);
1336                 length= linef->len - charf;
1337                 
1338                 buf[length++]='\n';
1339                 
1340                 tmp= linef->next;
1341                 while (tmp && tmp!=linel) {
1342                         strncpy(buf+length, tmp->line, tmp->len);
1343                         length+= tmp->len;
1344                         
1345                         buf[length++]='\n';                     
1346                         
1347                         tmp= tmp->next;
1348                 }
1349                 strncpy(buf+length, linel->line, charl);
1350                 length+= charl;
1351                 
1352                 /* python compiler wants an empty end line */
1353                 buf[length++]='\n';                     
1354                 buf[length]=0;
1355         }
1356         
1357         return buf;
1358 }
1359
1360 int txt_find_string(Text *text, const char *findstr, int wrap, int match_case)
1361 {
1362         TextLine *tl, *startl;
1363         char *s= NULL;
1364
1365         if (!text || !text->curl || !text->sell) return 0;
1366         
1367         txt_order_cursors(text);
1368
1369         tl= startl= text->sell;
1370         
1371         if (match_case) s= strstr(&tl->line[text->selc], findstr);
1372         else s= BLI_strcasestr(&tl->line[text->selc], findstr);
1373         while (!s) {
1374                 tl= tl->next;
1375                 if (!tl) {
1376                         if (wrap)
1377                                 tl= text->lines.first;
1378                         else
1379                                 break;
1380                 }
1381
1382                 if (match_case) s= strstr(tl->line, findstr);
1383                 else s= BLI_strcasestr(tl->line, findstr);
1384                 if (tl==startl)
1385                         break;
1386         }
1387         
1388         if (s) {
1389                 int newl= txt_get_span(text->lines.first, tl);
1390                 int newc= (int)(s-tl->line);
1391                 txt_move_to(text, newl, newc, 0);
1392                 txt_move_to(text, newl, newc + strlen(findstr), 1);
1393                 return 1;                               
1394         }
1395         else
1396                 return 0;
1397 }
1398
1399 char *txt_sel_to_buf (Text *text)
1400 {
1401         char *buf;
1402         int length=0;
1403         TextLine *tmp, *linef, *linel;
1404         int charf, charl;
1405         
1406         if (!text) return NULL;
1407         if (!text->curl) return NULL;
1408         if (!text->sell) return NULL;
1409         
1410         if (text->curl==text->sell) {
1411                 linef= linel= text->curl;
1412                 
1413                 if (text->curc < text->selc) {
1414                         charf= text->curc;
1415                         charl= text->selc;
1416                 }
1417                 else {
1418                         charf= text->selc;
1419                         charl= text->curc;
1420                 }
1421         }
1422         else if (txt_get_span(text->curl, text->sell)<0) {
1423                 linef= text->sell;
1424                 linel= text->curl;
1425
1426                 charf= text->selc;              
1427                 charl= text->curc;
1428         }
1429         else {
1430                 linef= text->curl;
1431                 linel= text->sell;
1432                 
1433                 charf= text->curc;
1434                 charl= text->selc;
1435         }
1436
1437         if (linef == linel) {
1438                 length= charl-charf;
1439
1440                 buf= MEM_mallocN(length+1, "sel buffer");
1441                 
1442                 BLI_strncpy(buf, linef->line + charf, length+1);
1443         }
1444         else {
1445                 length+= linef->len - charf;
1446                 length+= charl;
1447                 length++; /* For the '\n' */
1448                 
1449                 tmp= linef->next;
1450                 while (tmp && tmp!= linel) {
1451                         length+= tmp->len+1;
1452                         tmp= tmp->next;
1453                 }
1454                 
1455                 buf= MEM_mallocN(length+1, "sel buffer");
1456                 
1457                 strncpy(buf, linef->line+ charf, linef->len-charf);
1458                 length= linef->len-charf;
1459                 
1460                 buf[length++]='\n';
1461                 
1462                 tmp= linef->next;
1463                 while (tmp && tmp!=linel) {
1464                         strncpy(buf+length, tmp->line, tmp->len);
1465                         length+= tmp->len;
1466                         
1467                         buf[length++]='\n';                     
1468                         
1469                         tmp= tmp->next;
1470                 }
1471                 strncpy(buf+length, linel->line, charl);
1472                 length+= charl;
1473                 
1474                 buf[length]=0;
1475         }       
1476
1477         return buf;
1478 }
1479
1480 static void txt_shift_markers(Text *text, int lineno, int count)
1481 {
1482         TextMarker *marker;
1483
1484         for (marker=text->markers.first; marker; marker= marker->next)
1485                 if (marker->lineno>=lineno) {
1486                         marker->lineno+= count;
1487                 }
1488 }
1489
1490 void txt_insert_buf(Text *text, const char *in_buffer)
1491 {
1492         int l=0, u, len, lineno= -1, count= 0;
1493         size_t i=0, j;
1494         TextLine *add;
1495         char *buffer;
1496
1497         if (!text) return;
1498         if (!in_buffer) return;
1499
1500         txt_delete_sel(text);
1501         
1502         len= strlen(in_buffer);
1503         buffer= BLI_strdupn(in_buffer, len);
1504         len+= txt_extended_ascii_as_utf8(&buffer);
1505         
1506         if (!undoing) txt_undo_add_block(text, UNDO_IBLOCK, buffer);
1507
1508         u= undoing;
1509         undoing= 1;
1510
1511         /* Read the first line (or as close as possible */
1512         while (buffer[i] && buffer[i]!='\n')
1513                 txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, &i));
1514         
1515         if (buffer[i]=='\n') txt_split_curline(text);
1516         else { undoing = u; MEM_freeN(buffer); return; }
1517         i++;
1518
1519         /* Read as many full lines as we can */
1520         lineno= txt_get_span(text->lines.first, text->curl);
1521
1522         while (i<len) {
1523                 l=0;
1524
1525                 while (buffer[i] && buffer[i]!='\n') {
1526                         i++; l++;
1527                 }
1528         
1529                 if (buffer[i]=='\n') {
1530                         add= txt_new_linen(buffer +(i-l), l);
1531                         BLI_insertlinkbefore(&text->lines, text->curl, add);
1532                         i++;
1533                         count++;
1534                 }
1535                 else {
1536                         if (count) {
1537                                 txt_shift_markers(text, lineno, count);
1538                                 count= 0;
1539                         }
1540
1541                         for (j= i-l; j<i && j<len; )
1542                                 txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, &j));
1543                         break;
1544                 }
1545         }
1546         
1547         MEM_freeN(buffer);
1548
1549         if (count) {
1550                 txt_shift_markers(text, lineno, count);
1551         }
1552
1553         undoing= u;
1554 }
1555
1556 /******************/
1557 /* Undo functions */
1558 /******************/
1559
1560 static int max_undo_test(Text *text, int x)
1561 {
1562         while (text->undo_pos+x >= text->undo_len) {
1563                 if (text->undo_len*2 > TXT_MAX_UNDO) {
1564                         /* XXX error("Undo limit reached, buffer cleared\n"); */
1565                         MEM_freeN(text->undo_buf);
1566                         init_undo_text(text);
1567                         return 0;
1568                 }
1569                 else {
1570                         void *tmp= text->undo_buf;
1571                         text->undo_buf= MEM_callocN(text->undo_len*2, "undo buf");
1572                         memcpy(text->undo_buf, tmp, text->undo_len);
1573                         text->undo_len*=2;
1574                         MEM_freeN(tmp);
1575                 }
1576         }
1577
1578         return 1;
1579 }
1580
1581 static void dump_buffer(Text *text) 
1582 {
1583         int i= 0;
1584         
1585         while (i++<text->undo_pos) printf("%d: %d %c\n", i, text->undo_buf[i], text->undo_buf[i]);
1586 }
1587
1588 void txt_print_undo(Text *text)
1589 {
1590         int i= 0;
1591         int op;
1592         const char *ops;
1593         int linep, charp;
1594         
1595         dump_buffer(text);
1596         
1597         printf ("---< Undo Buffer >---\n");
1598         
1599         printf ("UndoPosition is %d\n", text->undo_pos);
1600         
1601         while (i<=text->undo_pos) {
1602                 op= text->undo_buf[i];
1603                 
1604                 if (op==UNDO_CLEFT) {
1605                         ops= "Cursor left";
1606                 }
1607                 else if (op==UNDO_CRIGHT) {
1608                         ops= "Cursor right";
1609                 }
1610                 else if (op==UNDO_CUP) {
1611                         ops= "Cursor up";
1612                 }
1613                 else if (op==UNDO_CDOWN) {
1614                         ops= "Cursor down";
1615                 }
1616                 else if (op==UNDO_SLEFT) {
1617                         ops= "Selection left";
1618                 }
1619                 else if (op==UNDO_SRIGHT) {
1620                         ops= "Selection right";
1621                 }
1622                 else if (op==UNDO_SUP) {
1623                         ops= "Selection up";
1624                 }
1625                 else if (op==UNDO_SDOWN) {
1626                         ops= "Selection down";
1627                 }
1628                 else if (op==UNDO_STO) {
1629                         ops= "Selection ";
1630                 }
1631                 else if (op==UNDO_CTO) {
1632                         ops= "Cursor ";
1633                 }
1634                 else if (op==UNDO_INSERT_1) {
1635                         ops= "Insert ascii ";
1636                 }
1637                 else if (op==UNDO_INSERT_2) {
1638                         ops= "Insert 2 bytes ";
1639                 }
1640                 else if (op==UNDO_INSERT_3) {
1641                         ops= "Insert 3 bytes ";
1642                 }
1643                 else if (op==UNDO_INSERT_4) {
1644                         ops= "Insert unicode ";
1645                 }
1646                 else if (op==UNDO_BS_1) {
1647                         ops= "Backspace for ascii ";
1648                 }
1649                 else if (op==UNDO_BS_2) {
1650                         ops= "Backspace for 2 bytes ";
1651                 }
1652                 else if (op==UNDO_BS_3) {
1653                         ops= "Backspace for 3 bytes ";
1654                 }
1655                 else if (op==UNDO_BS_4) {
1656                         ops= "Backspace for unicode ";
1657                 }
1658                 else if (op==UNDO_DEL_1) {
1659                         ops= "Delete ascii ";
1660                 }
1661                 else if (op==UNDO_DEL_2) {
1662                         ops= "Delete 2 bytes ";
1663                 }
1664                 else if (op==UNDO_DEL_3) {
1665                         ops= "Delete 3 bytes ";
1666                 }
1667                 else if (op==UNDO_DEL_4) {
1668                         ops= "Delete unicode ";
1669                 }
1670                 else if (op==UNDO_SWAP) {
1671                         ops= "Cursor swap";
1672                 }
1673                 else if (op==UNDO_DBLOCK) {
1674                         ops= "Delete text block";
1675                 }
1676                 else if (op==UNDO_IBLOCK) {
1677                         ops= "Insert text block";
1678                 }
1679                 else if (op==UNDO_INDENT) {
1680                         ops= "Indent ";
1681                 }
1682                 else if (op==UNDO_UNINDENT) {
1683                         ops= "Unindent ";
1684                 }
1685                 else if (op==UNDO_COMMENT) {
1686                         ops= "Comment ";
1687                 }
1688                 else if (op==UNDO_UNCOMMENT) {
1689                         ops= "Uncomment ";
1690                 }
1691                 else {
1692                         ops= "Unknown";
1693                 }
1694                 
1695                 printf ("Op (%o) at %d = %s", op, i, ops);
1696                 if (op >= UNDO_INSERT_1 && op <= UNDO_DEL_4) {
1697                         i++;
1698                         printf (" - Char is ");
1699                         switch (op) {
1700                                 case UNDO_INSERT_1: case UNDO_BS_1: case UNDO_DEL_1:
1701                                         printf ("%c", text->undo_buf[i]);  
1702                                         i++;
1703                                         break;
1704                                 case UNDO_INSERT_2: case UNDO_BS_2: case UNDO_DEL_2:
1705                                         printf ("%c%c", text->undo_buf[i], text->undo_buf[i+1]);  
1706                                         i+=2;
1707                                         break;
1708                                 case UNDO_INSERT_3: case UNDO_BS_3: case UNDO_DEL_3:
1709                                         printf ("%c%c%c", text->undo_buf[i], text->undo_buf[i+1], text->undo_buf[i+2]);  
1710                                         i+=3;
1711                                         break;
1712                                 case UNDO_INSERT_4: case UNDO_BS_4: case UNDO_DEL_4: {
1713                                         unsigned int uc;
1714                                         char c[BLI_UTF8_MAX+1];
1715                                         size_t c_len;
1716                                         uc= text->undo_buf[i]; i++;
1717                                         uc= uc+(text->undo_buf[i]<<8); i++;
1718                                         uc= uc+(text->undo_buf[i]<<16); i++;
1719                                         uc= uc+(text->undo_buf[i]<<24); i++;
1720                                         c_len= BLI_str_utf8_from_unicode(uc, c);
1721                                         c[c_len]= '\0';
1722                                         puts(c);
1723                                 }
1724                         }
1725                 }
1726                 else if (op==UNDO_STO || op==UNDO_CTO) {
1727                         i++;
1728
1729                         charp= text->undo_buf[i]; i++;
1730                         charp= charp+(text->undo_buf[i]<<8); i++;
1731
1732                         linep= text->undo_buf[i]; i++;
1733                         linep= linep+(text->undo_buf[i]<<8); i++;
1734                         linep= linep+(text->undo_buf[i]<<16); i++;
1735                         linep= linep+(text->undo_buf[i]<<24); i++;
1736                         
1737                         printf ("to <%d, %d> ", linep, charp);
1738
1739                         charp= text->undo_buf[i]; i++;
1740                         charp= charp+(text->undo_buf[i]<<8); i++;
1741
1742                         linep= text->undo_buf[i]; i++;
1743                         linep= linep+(text->undo_buf[i]<<8); i++;
1744                         linep= linep+(text->undo_buf[i]<<16); i++;
1745                         linep= linep+(text->undo_buf[i]<<24); i++;
1746                         
1747                         printf ("from <%d, %d>", linep, charp);
1748                 }
1749                 else if (op==UNDO_DBLOCK || op==UNDO_IBLOCK) {
1750                         i++;
1751
1752                         linep= text->undo_buf[i]; i++;
1753                         linep= linep+(text->undo_buf[i]<<8); i++;
1754                         linep= linep+(text->undo_buf[i]<<16); i++;
1755                         linep= linep+(text->undo_buf[i]<<24); i++;
1756                         
1757                         printf (" (length %d) <", linep);
1758                         
1759                         while (linep>0) {
1760                                 putchar(text->undo_buf[i]);
1761                                 linep--; i++;
1762                         }
1763                         
1764                         linep= text->undo_buf[i]; i++;
1765                         linep= linep+(text->undo_buf[i]<<8); i++;
1766                         linep= linep+(text->undo_buf[i]<<16); i++;
1767                         linep= linep+(text->undo_buf[i]<<24); i++;
1768                         printf ("> (%d)", linep);
1769                 }
1770                 else if (op==UNDO_INDENT || op==UNDO_UNINDENT) {
1771                         i++;
1772
1773                         charp= text->undo_buf[i]; i++;
1774                         charp= charp+(text->undo_buf[i]<<8); i++;
1775
1776                         linep= text->undo_buf[i]; i++;
1777                         linep= linep+(text->undo_buf[i]<<8); i++;
1778                         linep= linep+(text->undo_buf[i]<<16); i++;
1779                         linep= linep+(text->undo_buf[i]<<24); i++;
1780                         
1781                         printf ("to <%d, %d> ", linep, charp);
1782
1783                         charp= text->undo_buf[i]; i++;
1784                         charp= charp+(text->undo_buf[i]<<8); i++;
1785
1786                         linep= text->undo_buf[i]; i++;
1787                         linep= linep+(text->undo_buf[i]<<8); i++;
1788                         linep= linep+(text->undo_buf[i]<<16); i++;
1789                         linep= linep+(text->undo_buf[i]<<24); i++;
1790                         
1791                         printf ("from <%d, %d>", linep, charp);
1792                 }
1793                 
1794                 printf (" %d\n",  i);
1795                 i++;
1796         }
1797 }
1798
1799 static void txt_undo_add_op(Text *text, int op)
1800 {
1801         if (!max_undo_test(text, 2))
1802                 return;
1803         
1804         text->undo_pos++;
1805         text->undo_buf[text->undo_pos]= op;
1806         text->undo_buf[text->undo_pos+1]= 0;
1807 }
1808
1809 static void txt_undo_store_uint16(char *undo_buf, int *undo_pos, unsigned short value) 
1810 {
1811         undo_buf[*undo_pos]= (value)&0xff;
1812         (*undo_pos)++;
1813         undo_buf[*undo_pos]= (value>>8)&0xff;
1814         (*undo_pos)++;
1815 }
1816
1817 static void txt_undo_store_uint32(char *undo_buf, int *undo_pos, unsigned int value) 
1818 {
1819         undo_buf[*undo_pos]= (value)&0xff;
1820         (*undo_pos)++;
1821         undo_buf[*undo_pos]= (value>>8)&0xff;
1822         (*undo_pos)++;
1823         undo_buf[*undo_pos]= (value>>16)&0xff;
1824         (*undo_pos)++;
1825         undo_buf[*undo_pos]= (value>>24)&0xff;
1826         (*undo_pos)++;
1827 }
1828
1829 static void txt_undo_add_block(Text *text, int op, const char *buf)
1830 {
1831         unsigned int length= strlen(buf);
1832         
1833         if (!max_undo_test(text, length+11))
1834                 return;
1835
1836         text->undo_pos++;
1837         text->undo_buf[text->undo_pos]= op;
1838         text->undo_pos++;
1839         
1840         txt_undo_store_uint32(text->undo_buf, &text->undo_pos, length);
1841         
1842         strncpy(text->undo_buf+text->undo_pos, buf, length);
1843         text->undo_pos+=length;
1844
1845         txt_undo_store_uint32(text->undo_buf, &text->undo_pos, length);
1846         text->undo_buf[text->undo_pos]= op;
1847         
1848         text->undo_buf[text->undo_pos+1]= 0;
1849 }
1850
1851 void txt_undo_add_toop(Text *text, int op, unsigned int froml, unsigned short fromc, unsigned int tol, unsigned short toc)
1852 {
1853         if (!max_undo_test(text, 15))
1854                 return;
1855
1856         if (froml==tol && fromc==toc) return;
1857
1858         text->undo_pos++;
1859         text->undo_buf[text->undo_pos]= op;
1860
1861         text->undo_pos++;
1862         
1863         txt_undo_store_uint16(text->undo_buf, &text->undo_pos, fromc);
1864         txt_undo_store_uint32(text->undo_buf, &text->undo_pos, froml);
1865         txt_undo_store_uint16(text->undo_buf, &text->undo_pos, toc);
1866         txt_undo_store_uint32(text->undo_buf, &text->undo_pos, tol);
1867                 
1868         text->undo_buf[text->undo_pos]= op;
1869
1870         text->undo_buf[text->undo_pos+1]= 0;
1871 }
1872
1873 static void txt_undo_add_charop(Text *text, int op_start, unsigned int c)
1874 {
1875         char utf8[BLI_UTF8_MAX];
1876         size_t i, utf8_size = BLI_str_utf8_from_unicode(c, utf8);
1877         
1878         if (!max_undo_test(text, 3 + utf8_size))
1879                 return;
1880         
1881         text->undo_pos++;
1882         
1883         if (utf8_size < 4) {
1884                 text->undo_buf[text->undo_pos]= op_start + utf8_size - 1;
1885                 text->undo_pos++;
1886                 
1887                 for (i = 0; i < utf8_size; i++) {
1888                         text->undo_buf[text->undo_pos]= utf8[i];
1889                         text->undo_pos++;
1890                 }
1891                 
1892                 text->undo_buf[text->undo_pos]= op_start + utf8_size - 1;
1893         }
1894         else {
1895                 text->undo_buf[text->undo_pos]= op_start + 3;
1896                 text->undo_pos++;
1897                 txt_undo_store_uint32(text->undo_buf, &text->undo_pos, c);
1898                 text->undo_buf[text->undo_pos]= op_start + 3;
1899         }
1900         
1901         text->undo_buf[text->undo_pos+1]= 0;
1902 }
1903
1904 static unsigned short txt_undo_read_uint16(const char *undo_buf, int *undo_pos)
1905 {
1906         unsigned short val;
1907         val= undo_buf[*undo_pos]; (*undo_pos)--;
1908         val= (val<<8)+undo_buf[*undo_pos]; (*undo_pos)--;
1909         return val;
1910 }
1911
1912 static unsigned int txt_undo_read_uint32(const char *undo_buf, int *undo_pos)
1913 {
1914         unsigned int val;
1915         val= undo_buf[*undo_pos]; (*undo_pos)--;
1916         val= (val<<8)+undo_buf[*undo_pos]; (*undo_pos)--;
1917         val= (val<<8)+undo_buf[*undo_pos]; (*undo_pos)--;
1918         val= (val<<8)+undo_buf[*undo_pos]; (*undo_pos)--;
1919         return val;
1920 }
1921
1922 static unsigned int txt_undo_read_unicode(const char *undo_buf, int *undo_pos, short bytes)
1923 {
1924         unsigned int unicode;
1925         char utf8[BLI_UTF8_MAX+1];
1926         
1927         switch (bytes) {
1928                 case 1: /* ascii */
1929                         unicode = undo_buf[*undo_pos]; (*undo_pos)--; 
1930                         break;
1931                 case 2: /* 2-byte symbol */
1932                         utf8[2] = '\0';
1933                         utf8[1] = undo_buf[*undo_pos]; (*undo_pos)--;
1934                         utf8[0] = undo_buf[*undo_pos]; (*undo_pos)--;
1935                         unicode= BLI_str_utf8_as_unicode(utf8);
1936                         break;
1937                 case 3: /* 3-byte symbol */
1938                         utf8[3] = '\0';
1939                         utf8[2] = undo_buf[*undo_pos]; (*undo_pos)--;
1940                         utf8[1] = undo_buf[*undo_pos]; (*undo_pos)--;
1941                         utf8[0] = undo_buf[*undo_pos]; (*undo_pos)--;
1942                         unicode= BLI_str_utf8_as_unicode(utf8);
1943                         break;
1944                 case 4: /* 32-bit unicode symbol */
1945                         unicode= txt_undo_read_uint32(undo_buf, undo_pos);
1946                 default:
1947                         /* should never happen */
1948                         BLI_assert(0);
1949                         unicode= 0;
1950         }
1951         
1952         return unicode;
1953 }
1954
1955 static unsigned short txt_redo_read_uint16(const char *undo_buf, int *undo_pos)
1956 {
1957         unsigned short val;
1958         val = undo_buf[*undo_pos]; (*undo_pos)++;
1959         val = val+(undo_buf[*undo_pos]<<8); (*undo_pos)++;
1960         return val;
1961 }
1962
1963 static unsigned int txt_redo_read_uint32(const char *undo_buf, int *undo_pos)
1964 {
1965         unsigned int val;
1966         val= undo_buf[*undo_pos]; (*undo_pos)++;
1967         val= val+(undo_buf[*undo_pos]<<8); (*undo_pos)++;
1968         val= val+(undo_buf[*undo_pos]<<16); (*undo_pos)++;
1969         val= val+(undo_buf[*undo_pos]<<24); (*undo_pos)++;
1970         return val;
1971 }
1972
1973 static unsigned int txt_redo_read_unicode(const char *undo_buf, int *undo_pos, short bytes)
1974 {
1975         unsigned int unicode;
1976         char utf8[BLI_UTF8_MAX+1];
1977         
1978         switch (bytes) {
1979                 case 1: /* ascii */
1980                         unicode = undo_buf[*undo_pos]; (*undo_pos)++; 
1981                         break;
1982                 case 2: /* 2-byte symbol */
1983                         utf8[0] = undo_buf[*undo_pos]; (*undo_pos)++;
1984                         utf8[1] = undo_buf[*undo_pos]; (*undo_pos)++;
1985                         utf8[2] = '\0';
1986                         unicode= BLI_str_utf8_as_unicode(utf8);
1987                         break;
1988                 case 3: /* 3-byte symbol */
1989                         utf8[0] = undo_buf[*undo_pos]; (*undo_pos)++;
1990                         utf8[1] = undo_buf[*undo_pos]; (*undo_pos)++;
1991                         utf8[2] = undo_buf[*undo_pos]; (*undo_pos)++;
1992                         utf8[3] = '\0';
1993                         unicode= BLI_str_utf8_as_unicode(utf8);
1994                         break;
1995                 case 4: /* 32-bit unicode symbol */
1996                         unicode= txt_undo_read_uint32(undo_buf, undo_pos);
1997                 default:
1998                         /* should never happen */
1999                         BLI_assert(0);
2000                         unicode= 0;
2001         }
2002         
2003         return unicode;
2004 }
2005
2006 void txt_do_undo(Text *text)
2007 {
2008         int op= text->undo_buf[text->undo_pos];
2009         unsigned int linep, i;
2010         unsigned short charp;
2011         TextLine *holdl;
2012         int holdc, holdln;
2013         char *buf;
2014         
2015         if (text->undo_pos<0) {
2016                 return;
2017         }
2018
2019         text->undo_pos--;
2020
2021         undoing= 1;
2022         
2023         switch (op) {
2024                 case UNDO_CLEFT:
2025                         txt_move_right(text, 0);
2026                         break;
2027                         
2028                 case UNDO_CRIGHT:
2029                         txt_move_left(text, 0);
2030                         break;
2031                         
2032                 case UNDO_CUP:
2033                         txt_move_down(text, 0);
2034                         break;
2035                         
2036                 case UNDO_CDOWN:
2037                         txt_move_up(text, 0);
2038                         break;
2039
2040                 case UNDO_SLEFT:
2041                         txt_move_right(text, 1);
2042                         break;
2043
2044                 case UNDO_SRIGHT:
2045                         txt_move_left(text, 1);
2046                         break;
2047
2048                 case UNDO_SUP:
2049                         txt_move_down(text, 1);
2050                         break;
2051
2052                 case UNDO_SDOWN:
2053                         txt_move_up(text, 1);
2054                         break;
2055                 
2056                 case UNDO_CTO:
2057                 case UNDO_STO:
2058                         text->undo_pos--;
2059                         text->undo_pos--;
2060                         text->undo_pos--;
2061                         text->undo_pos--;
2062                 
2063                         text->undo_pos--;
2064                         text->undo_pos--;
2065                 
2066                         linep= txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
2067                         charp= txt_undo_read_uint16(text->undo_buf, &text->undo_pos);
2068                         
2069                         if (op==UNDO_CTO) {
2070                                 txt_move_toline(text, linep, 0);
2071                                 text->curc= charp;
2072                                 txt_pop_sel(text);
2073                         }
2074                         else {
2075                                 txt_move_toline(text, linep, 1);
2076                                 text->selc= charp;
2077                         }
2078                         
2079                         text->undo_pos--;
2080                         break;
2081                         
2082                 case UNDO_INSERT_1: case UNDO_INSERT_2: case UNDO_INSERT_3: case UNDO_INSERT_4:
2083                         txt_backspace_char(text);
2084                         text->undo_pos-= op - UNDO_INSERT_1 + 1;
2085                         text->undo_pos--;
2086                         break;
2087
2088                 case UNDO_BS_1: case UNDO_BS_2: case UNDO_BS_3: case UNDO_BS_4:
2089                         charp = op - UNDO_BS_1 + 1;
2090                         txt_add_char(text, txt_undo_read_unicode(text->undo_buf, &text->undo_pos, charp));
2091                         text->undo_pos--;
2092                         break;          
2093                         
2094                 case UNDO_DEL_1: case UNDO_DEL_2: case UNDO_DEL_3: case UNDO_DEL_4: 
2095                         charp = op - UNDO_DEL_1 + 1;
2096                         txt_add_char(text, txt_undo_read_unicode(text->undo_buf, &text->undo_pos, charp));
2097                         txt_move_left(text, 0);
2098                         text->undo_pos--;
2099                         break;
2100
2101                 case UNDO_SWAP:
2102                         txt_curs_swap(text);
2103                         break;
2104
2105                 case UNDO_DBLOCK:
2106                         linep= txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
2107
2108                         buf= MEM_mallocN(linep+1, "dblock buffer");
2109                         for (i=0; i < linep; i++) {
2110                                 buf[(linep-1)-i]= text->undo_buf[text->undo_pos]; 
2111                                 text->undo_pos--;
2112                         }
2113                         buf[i]= 0;
2114                         
2115                         txt_curs_first(text, &holdl, &holdc);
2116                         holdln= txt_get_span(text->lines.first, holdl);
2117                         
2118                         txt_insert_buf(text, buf);                      
2119                         MEM_freeN(buf);
2120
2121                         text->curl= text->lines.first;
2122                         while (holdln>0) {
2123                                 if (text->curl->next)
2124                                         text->curl= text->curl->next;
2125                                         
2126                                 holdln--;
2127                         }
2128                         text->curc= holdc;
2129
2130                         text->undo_pos--;
2131                         text->undo_pos--;
2132                         text->undo_pos--; 
2133                         text->undo_pos--;
2134
2135                         text->undo_pos--;
2136                         
2137                         break;
2138
2139                 case UNDO_IBLOCK:
2140                         linep= txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
2141                         txt_delete_sel(text);
2142
2143                         /* txt_backspace_char removes utf8-characters, not bytes */
2144                         buf= MEM_mallocN(linep+1, "iblock buffer");
2145                         for (i=0; i < linep; i++) {
2146                                 buf[(linep-1)-i]= text->undo_buf[text->undo_pos]; 
2147                                 text->undo_pos--;
2148                         }
2149                         buf[i]= 0;
2150                         linep= txt_utf8_len(buf);
2151                         MEM_freeN(buf);
2152
2153                         while (linep>0) {
2154                                 txt_backspace_char(text);
2155                                 linep--;
2156                         }
2157
2158                         text->undo_pos--;
2159                         text->undo_pos--;
2160                         text->undo_pos--; 
2161                         text->undo_pos--;
2162                         
2163                         text->undo_pos--;
2164
2165                         break;
2166                 case UNDO_INDENT:
2167                 case UNDO_UNINDENT:
2168                 case UNDO_COMMENT:
2169                 case UNDO_UNCOMMENT:
2170                         linep= txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
2171                         //linep is now the end line of the selection
2172                         
2173                         charp = txt_undo_read_uint16(text->undo_buf, &text->undo_pos);
2174                         //charp is the last char selected or text->line->len
2175                         
2176                         //set the selection for this now
2177                         text->selc = charp;
2178                         text->sell = text->lines.first;
2179                         for (i= 0; i < linep; i++) {
2180                                 text->sell = text->sell->next;
2181                         }
2182
2183                         linep= txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
2184                         //first line to be selected
2185                         
2186                         charp = txt_undo_read_uint16(text->undo_buf, &text->undo_pos);
2187                         //first postion to be selected
2188                         text->curc = charp;
2189                         text->curl = text->lines.first;
2190                         for (i = 0; i < linep; i++) {
2191                                 text->curl = text->curl->next;
2192                         }
2193
2194                         
2195                         if (op==UNDO_INDENT) {
2196                                 txt_unindent(text);
2197                         }
2198                         else if (op== UNDO_UNINDENT) {
2199                                 txt_indent(text);
2200                         }
2201                         else if (op == UNDO_COMMENT) {
2202                                 txt_uncomment(text);
2203                         }
2204                         else if (op == UNDO_UNCOMMENT) {
2205                                 txt_comment(text);
2206                         }
2207
2208                         text->undo_pos--;
2209                         break;
2210                 default:
2211                         //XXX error("Undo buffer error - resetting");
2212                         text->undo_pos= -1;
2213                         
2214                         break;
2215         }
2216
2217         /* next undo step may need evaluating */
2218         if (text->undo_pos>=0) {
2219                 switch (text->undo_buf[text->undo_pos]) {
2220                         case UNDO_STO:
2221                                 txt_do_undo(text);
2222                                 txt_do_redo(text); /* selections need restoring */
2223                                 break;
2224                         case UNDO_SWAP:
2225                                 txt_do_undo(text); /* swaps should appear transparent */
2226                                 break;
2227                 }
2228         }
2229         
2230         undoing= 0;     
2231 }
2232
2233 void txt_do_redo(Text *text)
2234 {
2235         char op;
2236         unsigned int linep, i;
2237         unsigned short charp;
2238         char *buf;
2239         
2240         text->undo_pos++;       
2241         op= text->undo_buf[text->undo_pos];
2242         
2243         if (!op) {
2244                 text->undo_pos--;
2245                 return;
2246         }
2247         
2248         undoing= 1;
2249
2250         switch (op) {
2251                 case UNDO_CLEFT:
2252                         txt_move_left(text, 0);
2253                         break;
2254                         
2255                 case UNDO_CRIGHT:
2256                         txt_move_right(text, 0);
2257                         break;
2258                         
2259                 case UNDO_CUP:
2260                         txt_move_up(text, 0);
2261                         break;
2262                         
2263                 case UNDO_CDOWN:
2264                         txt_move_down(text, 0);
2265                         break;
2266
2267                 case UNDO_SLEFT:
2268                         txt_move_left(text, 1);
2269                         break;
2270
2271                 case UNDO_SRIGHT:
2272                         txt_move_right(text, 1);
2273                         break;
2274
2275                 case UNDO_SUP:
2276                         txt_move_up(text, 1);
2277                         break;
2278
2279                 case UNDO_SDOWN:
2280                         txt_move_down(text, 1);
2281                         break;
2282                 
2283                 case UNDO_INSERT_1: case UNDO_INSERT_2: case UNDO_INSERT_3: case UNDO_INSERT_4:
2284                         text->undo_pos++;
2285                         charp = op - UNDO_INSERT_1 + 1;
2286                         txt_add_char(text, txt_redo_read_unicode(text->undo_buf, &text->undo_pos, charp));
2287                         break;
2288
2289                 case UNDO_BS_1: case UNDO_BS_2: case UNDO_BS_3: case UNDO_BS_4:
2290                         text->undo_pos++;
2291                         txt_backspace_char(text);
2292                         text->undo_pos+= op - UNDO_BS_1 + 1;
2293                         break;
2294
2295                 case UNDO_DEL_1: case UNDO_DEL_2: case UNDO_DEL_3: case UNDO_DEL_4:
2296                         text->undo_pos++;
2297                         txt_delete_char(text);
2298                         text->undo_pos+= op - UNDO_DEL_1 + 1;
2299                         break;
2300
2301                 case UNDO_SWAP:
2302                         txt_curs_swap(text);
2303                         txt_do_redo(text); /* swaps should appear transparent a*/
2304                         break;
2305                         
2306                 case UNDO_CTO:
2307                 case UNDO_STO:
2308                         text->undo_pos++;
2309                         text->undo_pos++;
2310
2311                         text->undo_pos++;
2312                         text->undo_pos++;
2313                         text->undo_pos++;
2314                         text->undo_pos++;
2315
2316                         text->undo_pos++;
2317
2318                         charp= txt_redo_read_uint16(text->undo_buf, &text->undo_pos);
2319                         linep= txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
2320                         
2321                         if (op==UNDO_CTO) {
2322                                 txt_move_toline(text, linep, 0);
2323                                 text->curc= charp;
2324                                 txt_pop_sel(text);
2325                         }
2326                         else {
2327                                 txt_move_toline(text, linep, 1);
2328                                 text->selc= charp;
2329                         }
2330
2331                         break;
2332
2333                 case UNDO_DBLOCK:
2334                         text->undo_pos++;
2335                         linep= txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
2336                         txt_delete_sel(text);
2337                         
2338                         text->undo_pos+=linep;
2339
2340                         text->undo_pos++;
2341                         text->undo_pos++;
2342                         text->undo_pos++; 
2343                         text->undo_pos++;
2344                         
2345                         break;
2346
2347                 case UNDO_IBLOCK:
2348                         text->undo_pos++;
2349                         linep= txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
2350
2351                         buf= MEM_mallocN(linep+1, "iblock buffer");
2352                         memcpy (buf, &text->undo_buf[text->undo_pos], linep);
2353                         text->undo_pos+= linep;
2354                         buf[linep]= 0;
2355                         
2356                         txt_insert_buf(text, buf);                      
2357                         MEM_freeN(buf);
2358
2359                         text->undo_pos++;
2360                         text->undo_pos++;
2361                         text->undo_pos++; 
2362                         text->undo_pos++;
2363                         break;
2364                         
2365                 case UNDO_INDENT:
2366                 case UNDO_UNINDENT:
2367                 case UNDO_COMMENT:
2368                 case UNDO_UNCOMMENT:
2369                         text->undo_pos++;
2370                         charp = txt_redo_read_uint16(text->undo_buf, &text->undo_pos);
2371                         //charp is the first char selected or 0
2372                         
2373                         linep= txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
2374                         //linep is now the first line of the selection                  
2375                         //set the selcetion for this now
2376                         text->curc = charp;
2377                         text->curl = text->lines.first;
2378                         for (i= 0; i < linep; i++) {
2379                                 text->curl = text->curl->next;
2380                         }
2381                         
2382                         charp = txt_redo_read_uint16(text->undo_buf, &text->undo_pos);
2383                         //last postion to be selected
2384                         
2385                         linep= txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
2386                         //Last line to be selected
2387                         
2388                         text->selc = charp;
2389                         text->sell = text->lines.first;
2390                         for (i = 0; i < linep; i++) {
2391                                 text->sell = text->sell->next;
2392                         }
2393
2394                         if (op==UNDO_INDENT) {
2395                                 txt_indent(text);
2396                         }
2397                         else if (op== UNDO_UNINDENT) {
2398                                 txt_unindent(text);
2399                         }
2400                         else if (op == UNDO_COMMENT) {
2401                                 txt_comment(text);
2402                         }
2403                         else if (op == UNDO_UNCOMMENT) {
2404                                 txt_uncomment(text);
2405                         }
2406                         break;
2407                 default:
2408                         //XXX error("Undo buffer error - resetting");
2409                         text->undo_pos= -1;
2410
2411                         break;
2412         }
2413         
2414         undoing= 0;     
2415 }
2416
2417 /**************************/
2418 /* Line editing functions */ 
2419 /**************************/
2420
2421 void txt_split_curline (Text *text) 
2422 {
2423         TextLine *ins;
2424         TextMarker *mrk;
2425         char *left, *right;
2426         int lineno= -1;
2427         
2428         if (!text) return;
2429         if (!text->curl) return;
2430
2431         txt_delete_sel(text);
2432
2433         /* Move markers */
2434
2435         lineno= txt_get_span(text->lines.first, text->curl);
2436         mrk= text->markers.first;
2437         while (mrk) {
2438                 if (mrk->lineno==lineno && mrk->start>text->curc) {
2439                         mrk->lineno++;
2440                         mrk->start -= text->curc;
2441                         mrk->end -= text->curc;
2442                 }
2443                 else if (mrk->lineno > lineno) {
2444                         mrk->lineno++;
2445                 }
2446                 mrk= mrk->next;
2447         }
2448
2449         /* Make the two half strings */
2450
2451         left= MEM_mallocN(text->curc+1, "textline_string");
2452         if (text->curc) memcpy(left, text->curl->line, text->curc);
2453         left[text->curc]=0;
2454         
2455         right= MEM_mallocN(text->curl->len - text->curc+1, "textline_string");
2456         memcpy(right, text->curl->line+text->curc, text->curl->len-text->curc+1);
2457
2458         MEM_freeN(text->curl->line);
2459         if (text->curl->format) MEM_freeN(text->curl->format);
2460
2461         /* Make the new TextLine */
2462         
2463         ins= MEM_mallocN(sizeof(TextLine), "textline");
2464         ins->line= left;
2465         ins->format= NULL;
2466         ins->len= text->curc;
2467         
2468         text->curl->line= right;
2469         text->curl->format= NULL;
2470         text->curl->len= text->curl->len - text->curc;
2471         
2472         BLI_insertlinkbefore(&text->lines, text->curl, ins);    
2473         
2474         text->curc=0;
2475         
2476         txt_make_dirty(text);
2477         txt_clean_text(text);
2478         
2479         txt_pop_sel(text);
2480         if (!undoing) txt_undo_add_charop(text, UNDO_INSERT_1, '\n');
2481 }
2482
2483 static void txt_delete_line (Text *text, TextLine *line) 
2484 {
2485         TextMarker *mrk=NULL, *nxt;
2486         int lineno= -1;
2487
2488         if (!text) return;
2489         if (!text->curl) return;
2490
2491         lineno= txt_get_span(text->lines.first, line);
2492         mrk= text->markers.first;
2493         while (mrk) {
2494                 nxt= mrk->next;
2495                 if (mrk->lineno==lineno)
2496                         BLI_freelinkN(&text->markers, mrk);
2497                 else if (mrk->lineno > lineno)
2498                         mrk->lineno--;
2499                 mrk= nxt;
2500         }
2501
2502         BLI_remlink (&text->lines, line);
2503         
2504         if (line->line) MEM_freeN(line->line);
2505         if (line->format) MEM_freeN(line->format);
2506
2507         MEM_freeN(line);
2508
2509         txt_make_dirty(text);
2510         txt_clean_text(text);
2511 }
2512
2513 static void txt_combine_lines (Text *text, TextLine *linea, TextLine *lineb)
2514 {
2515         char *tmp;
2516         TextMarker *mrk= NULL;
2517         int lineno=-1;
2518         
2519         if (!text) return;
2520         
2521         if (!linea || !lineb) return;
2522
2523         mrk= txt_find_marker_region(text, lineb, 0, lineb->len, 0, 0);
2524         if (mrk) {
2525                 lineno= mrk->lineno;
2526                 do {
2527                         mrk->lineno--;
2528                         mrk->start += linea->len;
2529                         mrk->end += linea->len;
2530                         mrk= mrk->next;
2531                 } while (mrk && mrk->lineno==lineno);
2532         }
2533         if (lineno==-1) lineno= txt_get_span(text->lines.first, lineb);
2534         
2535         tmp= MEM_mallocN(linea->len+lineb->len+1, "textline_string");
2536         
2537         strcpy(tmp, linea->line);
2538         strcat(tmp, lineb->line);
2539
2540         make_new_line(linea, tmp);
2541         
2542         txt_delete_line(text, lineb);
2543         
2544         txt_make_dirty(text);
2545         txt_clean_text(text);
2546 }
2547
2548 void txt_delete_char(Text *text) 
2549 {
2550         unsigned int c='\n';
2551         
2552         if (!text) return;
2553         if (!text->curl) return;
2554
2555         if (txt_has_sel(text)) { /* deleting a selection */
2556                 txt_delete_sel(text);
2557                 txt_make_dirty(text);
2558                 return;
2559         }
2560         else if (text->curc== text->curl->len) { /* Appending two lines */
2561                 if (text->curl->next) {
2562                         txt_combine_lines(text, text->curl, text->curl->next);
2563                         txt_pop_sel(text);
2564                 }
2565                 else
2566                         return;
2567         }
2568         else { /* Just deleting a char */
2569                 size_t c_len = 0;
2570                 TextMarker *mrk;
2571                 c= BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &c_len);
2572
2573                 mrk= txt_find_marker_region(text, text->curl, text->curc - c_len, text->curl->len, 0, 0);
2574                 if (mrk) {
2575                         int lineno= mrk->lineno;
2576                         if (mrk->end==text->curc) {
2577                                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
2578                                         txt_clear_markers(text, mrk->group, TMARK_TEMP);
2579                                 }
2580                                 else {
2581                                         BLI_freelinkN(&text->markers, mrk);
2582                                 }
2583                                 return;
2584                         }
2585                         do {
2586                                 if (mrk->start>text->curc) mrk->start-= c_len;
2587                                 mrk->end-= c_len;
2588                                 mrk= mrk->next;
2589                         } while (mrk && mrk->lineno==lineno);
2590                 }
2591                 
2592                 memmove(text->curl->line+text->curc, text->curl->line+text->curc+c_len, text->curl->len-text->curc-c_len+1);
2593
2594                 text->curl->len-= c_len;
2595
2596                 txt_pop_sel(text);
2597         }
2598
2599         txt_make_dirty(text);
2600         txt_clean_text(text);
2601         
2602         if (!undoing) txt_undo_add_charop(text, UNDO_DEL_1, c);
2603 }
2604
2605 void txt_delete_word (Text *text) 
2606 {
2607         txt_jump_right(text, 1);
2608         txt_delete_sel(text);
2609 }
2610
2611 void txt_backspace_char (Text *text) 
2612 {
2613         unsigned int c='\n';
2614         
2615         if (!text) return;
2616         if (!text->curl) return;
2617         
2618         if (txt_has_sel(text)) { /* deleting a selection */
2619                 txt_delete_sel(text);
2620                 txt_make_dirty(text);
2621                 return;
2622         }
2623         else if (text->curc==0) { /* Appending two lines */
2624                 if (!text->curl->prev) return;
2625                 
2626                 text->curl= text->curl->prev;
2627                 text->curc= text->curl->len;
2628                 
2629                 txt_combine_lines(text, text->curl, text->curl->next);
2630                 txt_pop_sel(text);
2631         }
2632         else { /* Just backspacing a char */
2633                 size_t c_len = 0;
2634                 TextMarker *mrk;
2635                 char *prev = BLI_str_prev_char_utf8(text->curl->line + text->curc);
2636                 c= BLI_str_utf8_as_unicode_and_size(prev, &c_len);
2637
2638                 mrk= txt_find_marker_region(text, text->curl, text->curc - c_len, text->curl->len, 0, 0);
2639                 if (mrk) {
2640                         int lineno= mrk->lineno;
2641                         if (mrk->start==text->curc) {
2642                                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
2643                                         txt_clear_markers(text, mrk->group, TMARK_TEMP);
2644                                 }
2645                                 else {
2646                                         BLI_freelinkN(&text->markers, mrk);
2647                                 }
2648                                 return;
2649                         }
2650                         do {
2651                                 if (mrk->start>text->curc - c_len) mrk->start-= c_len;
2652                                 mrk->end-= c_len;
2653                                 mrk= mrk->next;
2654                         } while (mrk && mrk->lineno==lineno);
2655                 }
2656                 
2657                 /* source and destination overlap, don't use memcpy() */
2658                 memmove(text->curl->line + text->curc - c_len,
2659                         text->curl->line + text->curc,
2660                         text->curl->len  - text->curc + 1);
2661
2662                 text->curl->len-= c_len;
2663                 text->curc-= c_len;
2664
2665                 txt_pop_sel(text);
2666         }
2667
2668         txt_make_dirty(text);
2669         txt_clean_text(text);
2670         
2671         if (!undoing) txt_undo_add_charop(text, UNDO_BS_1, c);
2672 }
2673
2674 void txt_backspace_word (Text *text) 
2675 {
2676         txt_jump_left(text, 1);
2677         txt_delete_sel(text);
2678 }
2679
2680 /* Max spaces to replace a tab with, currently hardcoded to TXT_TABSIZE = 4.
2681  * Used by txt_convert_tab_to_spaces, indent and unindent.
2682  * Remember to change this string according to max tab size */
2683 static char tab_to_spaces[] = "    ";
2684
2685 static void txt_convert_tab_to_spaces (Text *text)
2686 {
2687         /* sb aims to pad adjust the tab-width needed so that the right number of spaces
2688          * is added so that the indention of the line is the right width (i.e. aligned
2689          * to multiples of TXT_TABSIZE)
2690          */
2691         char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
2692         txt_insert_buf(text, sb);
2693 }
2694
2695 static int txt_add_char_intern (Text *text, unsigned int add, int replace_tabs)
2696 {
2697         int lineno;
2698         char *tmp, ch[BLI_UTF8_MAX];
2699         TextMarker *mrk;
2700         size_t add_len;
2701         
2702         if (!text) return 0;
2703         if (!text->curl) return 0;
2704
2705         if (add=='\n') {
2706                 txt_split_curline(text);
2707                 return 1;
2708         }
2709         
2710         /* insert spaces rather than tabs */
2711         if (add == '\t' && replace_tabs) {
2712                 txt_convert_tab_to_spaces(text);
2713                 return 1;
2714         }
2715
2716         txt_delete_sel(text);
2717         
2718         add_len = BLI_str_utf8_from_unicode(add, ch);
2719         mrk= txt_find_marker_region(text, text->curl, text->curc-1, text->curl->len, 0, 0);
2720         if (mrk) {
2721                 lineno= mrk->lineno;
2722                 do {
2723                         if (mrk->start>text->curc) mrk->start+= add_len;
2724                         mrk->end+= add_len;
2725                         mrk= mrk->next;
2726                 } while (mrk && mrk->lineno==lineno);
2727         }
2728         
2729         tmp= MEM_mallocN(text->curl->len+add_len+1, "textline_string");
2730         
2731         memcpy(tmp, text->curl->line, text->curc);
2732         memcpy(tmp+text->curc, ch, add_len);
2733         memcpy(tmp+text->curc+add_len, text->curl->line+text->curc, text->curl->len-text->curc+1);
2734
2735         make_new_line(text->curl, tmp);
2736                 
2737         text->curc+= add_len;
2738
2739         txt_pop_sel(text);
2740         
2741         txt_make_dirty(text);
2742         txt_clean_text(text);
2743
2744         if (!undoing) txt_undo_add_charop(text, UNDO_INSERT_1, add);
2745         return 1;
2746 }
2747
2748 int txt_add_char (Text *text, unsigned int add)
2749 {
2750         return txt_add_char_intern(text, add, text->flags & TXT_TABSTOSPACES);
2751 }
2752
2753 int txt_add_raw_char (Text *text, unsigned int add)
2754 {
2755         return txt_add_char_intern(text, add, 0);
2756 }
2757
2758 void txt_delete_selected(Text *text)
2759 {
2760         txt_delete_sel(text);
2761         txt_make_dirty(text);
2762 }
2763
2764 int txt_replace_char (Text *text, unsigned int add)
2765 {
2766         unsigned int del;
2767         size_t del_size = 0, add_size;
2768         char ch[BLI_UTF8_MAX];
2769         
2770         if (!text) return 0;
2771         if (!text->curl) return 0;
2772
2773         /* If text is selected or we're at the end of the line just use txt_add_char */
2774         if (text->curc==text->curl->len || txt_has_sel(text) || add=='\n') {
2775                 int i= txt_add_char(text, add);
2776                 TextMarker *mrk= txt_find_marker(text, text->curl, text->curc, 0, 0);
2777                 if (mrk) BLI_freelinkN(&text->markers, mrk);
2778                 return i;
2779         }
2780         
2781         del= BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &del_size);
2782         add_size= BLI_str_utf8_from_unicode(add, ch);
2783         
2784         if (add_size > del_size) {
2785                 char *tmp= MEM_mallocN(text->curl->len+add_size-del_size+1, "textline_string");
2786                 memcpy(tmp, text->curl->line, text->curc);
2787                 memcpy(tmp+text->curc+add_size, text->curl->line+text->curc+del_size, text->curl->len-text->curc-del_size+1);
2788                 MEM_freeN(text->curl->line);
2789                 text->curl->line = tmp;
2790         }
2791         else if (add_size < del_size) {
2792                 char *tmp= text->curl->line;
2793                 memmove(tmp+text->curc+add_size, tmp+text->curc+del_size, text->curl->len-text->curc-del_size+1);
2794         }
2795         
2796         memcpy(text->curl->line + text->curc, ch, add_size);
2797         text->curc+= add_size;
2798         
2799         txt_pop_sel(text);
2800         txt_make_dirty(text);
2801         txt_clean_text(text);
2802
2803         /* Should probably create a new op for this */
2804         if (!undoing) {
2805                 txt_undo_add_charop(text, UNDO_DEL_1, del);
2806                 txt_undo_add_charop(text, UNDO_INSERT_1, add);
2807         }
2808         return 1;
2809 }
2810
2811 void txt_indent(Text *text)
2812 {
2813         int len, num;
2814         char *tmp;
2815
2816         const char *add = "\t";
2817         int indentlen = 1;
2818         
2819         /* hardcoded: TXT_TABSIZE = 4 spaces: */
2820         int spaceslen = TXT_TABSIZE;
2821
2822         if (ELEM3(NULL, text, text->curl, text->sell)) {
2823                 return;
2824         }
2825
2826         if (!text) return;
2827         if (!text->curl) return;
2828         if (!text->sell) return;
2829
2830         /* insert spaces rather than tabs */
2831         if (text->flags & TXT_TABSTOSPACES) {
2832                 add = tab_to_spaces;
2833                 indentlen = spaceslen;
2834         }
2835
2836         num = 0;
2837         while (TRUE) {
2838                 tmp= MEM_mallocN(text->curl->len+indentlen+1, "textline_string");
2839                 
2840                 text->curc = 0; 
2841                 if (text->curc) memcpy(tmp, text->curl->line, text->curc); /* XXX never true, check prev line */
2842                 memcpy(tmp+text->curc, add, indentlen);
2843                 
2844                 len= text->curl->len - text->curc;
2845                 if (len>0) memcpy(tmp+text->curc+indentlen, text->curl->line+text->curc, len);
2846                 tmp[text->curl->len+indentlen]= 0;
2847
2848                 make_new_line(text->curl, tmp);
2849                         
2850                 text->curc+= indentlen;
2851                 
2852                 txt_make_dirty(text);
2853                 txt_clean_text(text);
2854                 
2855                 if (text->curl == text->sell) {
2856                         text->selc = text->sell->len;
2857                         break;
2858                 }
2859                 else {
2860                         text->curl = text->curl->next;
2861                         num++;
2862                 }
2863         }
2864         text->curc = 0;
2865         while ( num > 0 ) {
2866                 text->curl = text->curl->prev;
2867                 num--;
2868         }
2869         
2870         if (!undoing) {
2871                 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);
2872         }
2873 }
2874
2875 void txt_unindent(Text *text)
2876 {
2877         int num = 0;
2878         const char *remove = "\t";
2879         int indent = 1;
2880         
2881         /* hardcoded: TXT_TABSIZE = 4 spaces: */
2882         int spaceslen = TXT_TABSIZE;
2883
2884         if (!text) return;
2885         if (!text->curl) return;
2886         if (!text->sell) return;
2887
2888         /* insert spaces rather than tabs */
2889         if (text->flags & TXT_TABSTOSPACES) {
2890                 remove = tab_to_spaces;
2891                 indent = spaceslen;
2892         }
2893
2894         while (TRUE) {
2895                 int i = 0;
2896                 
2897                 if (BLI_strncasecmp(text->curl->line, remove, indent) == 0) {
2898                         while (i< text->curl->len) {
2899                                 text->curl->line[i]= text->curl->line[i+indent];
2900                                 i++;
2901                         }
2902                         text->curl->len-= indent;
2903                 }
2904         
2905                 txt_make_dirty(text);
2906                 txt_clean_text(text);
2907                 
2908                 if (text->curl == text->sell) {
2909                         text->selc = text->sell->len;
2910                         break;
2911                 }
2912                 else {
2913                         text->curl = text->curl->next;
2914                         num++;
2915                 }
2916                 
2917         }
2918         text->curc = 0;
2919         while ( num > 0 ) {
2920                 text->curl = text->curl->prev;
2921                 num--;
2922         }
2923         
2924         if (!undoing) {
2925                 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);
2926         }
2927 }
2928
2929 void txt_comment(Text *text)
2930 {
2931         int len, num;
2932         char *tmp;
2933         char add = '#';
2934         
2935         if (!text) return;
2936         if (!text->curl) return;
2937         if (!text->sell) return;// Need to change this need to check if only one line is selected to more then one
2938
2939         num = 0;
2940         while (TRUE) {
2941                 tmp= MEM_mallocN(text->curl->len+2, "textline_string");
2942                 
2943                 text->curc = 0; 
2944                 if (text->curc) memcpy(tmp, text->curl->line, text->curc);
2945                 tmp[text->curc]= add;
2946                 
2947                 len= text->curl->len - text->curc;
2948                 if (len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
2949                 tmp[text->curl->len+1]=0;
2950
2951                 make_new_line(text->curl, tmp);
2952                         
2953                 text->curc++;
2954                 
2955                 txt_make_dirty(text);
2956                 txt_clean_text(text);
2957                 
2958                 if (text->curl == text->sell) {
2959                         text->selc = text->sell->len;
2960                         break;
2961                 }
2962                 else {
2963                         text->curl = text->curl->next;
2964                         num++;
2965                 }
2966         }
2967         text->curc = 0;
2968         while ( num > 0 ) {
2969                 text->curl = text->curl->prev;
2970                 num--;
2971         }
2972         
2973         if (!undoing) {
2974                 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);
2975         }
2976 }
2977
2978 void txt_uncomment(Text *text)
2979 {
2980         int num = 0;
2981         char remove = '#';
2982         
2983         if (!text) return;
2984         if (!text->curl) return;
2985         if (!text->sell) return;
2986
2987         while (TRUE) {
2988                 int i = 0;
2989                 
2990                 if (text->curl->line[i] == remove) {
2991                         while (i< text->curl->len) {
2992                                 text->curl->line[i]= text->curl->line[i+1];
2993                                 i++;
2994                         }
2995                         text->curl->len--;
2996                 }
2997                          
2998         
2999                 txt_make_dirty(text);
3000                 txt_clean_text(text);
3001                 
3002                 if (text->curl == text->sell) {
3003                         text->selc = text->sell->len;
3004                         break;
3005                 }
3006                 else {
3007                         text->curl = text->curl->next;
3008                         num++;
3009                 }
3010                 
3011         }
3012         text->curc = 0;
3013         while ( num > 0 ) {
3014                 text->curl = text->curl->prev;
3015                 num--;
3016         }
3017         
3018         if (!undoing) {
3019                 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);
3020         }
3021 }
3022
3023 int setcurr_tab_spaces (Text *text, int space)
3024 {
3025         int i = 0;
3026         int test = 0;
3027         const char *word = ":";
3028         const char *comm = "#";
3029         const char indent= (text->flags & TXT_TABSTOSPACES) ? ' ' : '\t';
3030         static const char *back_words[]= {"return", "break", "continue", "pass", "yield", NULL};
3031         if (!text) return 0;
3032         if (!text->curl) return 0;
3033
3034         while (text->curl->line[i] == indent) {
3035                 //we only count those tabs/spaces that are before any text or before the curs;
3036                 if (i == text->curc) {
3037                         return i;
3038                 }
3039                 else {
3040                         i++;
3041                 }
3042         }
3043         if (strstr(text->curl->line, word)) {
3044                 /* if we find a ':' on this line, then add a tab but not if it is:
3045                  *      1) in a comment
3046                  *      2) within an identifier
3047                  *      3) after the cursor (text->curc), i.e. when creating space before a function def [#25414] 
3048                  */
3049                 int a, is_indent = 0;
3050                 for (a=0; (a < text->curc) && (text->curl->line[a] != '\0'); a++) {
3051                         char ch= text->curl->line[a];
3052                         if (ch=='#') {
3053                                 break;
3054                         }
3055                         else if (ch==':') {
3056                                 is_indent = 1;
3057                         }
3058                         else if (ch!=' ' && ch!='\t') {
3059                                 is_indent = 0;
3060                         }
3061                 }
3062                 if (is_indent) {
3063                         i += space;
3064                 }
3065         }
3066
3067         for (test=0; back_words[test]; test++) {
3068                 /* if there are these key words then remove a tab because we are done with the block */
3069                 if (strstr(text->curl->line, back_words[test]) && i > 0) {
3070                         if (strcspn(text->curl->line, back_words[test]) < strcspn(text->curl->line, comm)) {
3071                                 i -= space;
3072                         }
3073                 }
3074         }
3075         return i;
3076 }
3077
3078 /*********************************/
3079 /* Text marker utility functions */
3080 /*********************************/
3081
3082 /* Creates and adds a marker to the list maintaining sorted order */
3083 void txt_add_marker(Text *text, TextLine *line, int start, int end, const unsigned char color[4], int group, int flags)
3084 {
3085         TextMarker *tmp, *marker;
3086
3087         marker= MEM_mallocN(sizeof(TextMarker), "text_marker");
3088         
3089         marker->lineno= txt_get_span(text->lines.first, line);
3090         marker->start= MIN2(start, end);
3091         marker->end= MAX2(start, end);
3092         marker->group= group;
3093         marker->flags= flags;
3094
3095         marker->color[0]= color[0];
3096         marker->color[1]= color[1];
3097         marker->color[2]= color[2];
3098         marker->color[3]= color[3];
3099
3100         for (tmp=text->markers.last; tmp; tmp=tmp->prev)
3101                 if (tmp->lineno < marker->lineno || (tmp->lineno==marker->lineno && tmp->start < marker->start))
3102                         break;
3103
3104         if (tmp) BLI_insertlinkafter(&text->markers, tmp, marker);
3105         else BLI_addhead(&text->markers, marker);
3106 }
3107
3108 /* Returns the first matching marker on the specified line between two points.
3109  * If the group or flags fields are non-zero the returned flag must be in the
3110  * specified group and have at least the specified flags set. */
3111 TextMarker *txt_find_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags)
3112 {
3113         TextMarker *marker, *next;
3114         int lineno= txt_get_span(text->lines.first, line);
3115         
3116         for (marker=text->markers.first; marker; marker=next) {
3117                 next= marker->next;
3118
3119                 if      (group && marker->group != group)  continue;
3120                 else if ((marker->flags & flags) != flags) continue;
3121                 else if (marker->lineno < lineno)          continue;
3122                 else if (marker->lineno > lineno) break;
3123
3124                 if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
3125                     (marker->start<end && marker->end>start))
3126                 {
3127                         return marker;
3128                 }
3129         }
3130         return NULL;
3131 }
3132
3133 /* Clears all markers on the specified line between two points. If the group or
3134  * flags fields are non-zero the returned flag must be in the specified group
3135  * and have at least the specified flags set. */
3136 short txt_clear_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags)
3137 {
3138         TextMarker *marker, *next;
3139         int lineno= txt_get_span(text->lines.first, line);
3140         short cleared= 0;
3141         
3142         for (marker=text->markers.first; marker; marker=next) {
3143                 next= marker->next;
3144
3145                 if (group && marker->group != group) continue;
3146                 else if ((marker->flags & flags) != flags) continue;
3147                 else if (marker->lineno < lineno) continue;
3148                 else if (marker->lineno > lineno) break;
3149
3150                 if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
3151                         (marker->start<end && marker->end>start)) {
3152                         BLI_freelinkN(&text->markers, marker);
3153                         cleared= 1;
3154                 }
3155         }
3156         return cleared;
3157 }
3158
3159 /* Clears all markers in the specified group (if given) with at least the
3160  * specified flags set. Useful for clearing temporary markers (group=0,
3161  * flags=TMARK_TEMP) */
3162 short txt_clear_markers(Text *text, int group, int flags)
3163 {
3164         TextMarker *marker, *next;
3165         short cleared= 0;
3166         
3167         for (marker=text->markers.first; marker; marker=next) {
3168                 next= marker->next;
3169
3170                 if ((!group || marker->group==group) &&
3171                                 (marker->flags & flags) == flags) {
3172                         BLI_freelinkN(&text->markers, marker);
3173                         cleared= 1;
3174                 }
3175         }
3176         return cleared;
3177 }
3178
3179 /* Finds the marker at the specified line and cursor position with at least the
3180  * specified flags set in the given group (if non-zero). */
3181 TextMarker *txt_find_marker(Text *text, TextLine *line, int curs, int group, int flags)
3182 {
3183         TextMarker *marker;
3184         int lineno= txt_get_span(text->lines.first, line);
3185         
3186         for (marker=text->markers.first; marker; marker=marker->next) {
3187                 if (group && marker->group != group) continue;
3188                 else if ((marker->flags & flags) != flags) continue;
3189                 else if (marker->lineno < lineno) continue;
3190                 else if (marker->lineno > lineno) break;
3191
3192                 if (marker->start <= curs && curs <= marker->end)
3193                         return marker;
3194         }
3195         return NULL;
3196 }
3197
3198 /* Finds the previous marker in the same group. If no other is found, the same
3199  * marker will be returned */
3200 TextMarker *txt_prev_marker(Text *text, TextMarker *marker)
3201 {
3202         TextMarker *tmp= marker;
3203         while (tmp) {
3204                 if (tmp->prev) tmp= tmp->prev;
3205                 else tmp= text->markers.last;
3206                 if (tmp->group == marker->group)
3207                         return tmp;
3208         }
3209         return NULL; /* Only if marker==NULL */
3210 }
3211
3212 /* Finds the next marker in the same group. If no other is found, the same
3213  * marker will be returned */
3214 TextMarker *txt_next_marker(Text *text, TextMarker *marker)
3215 {
3216         TextMarker *tmp= marker;
3217         while (tmp) {
3218                 if (tmp->next) tmp= tmp->next;
3219                 else tmp= text->markers.first;
3220                 if (tmp->group == marker->group)
3221                         return tmp;
3222         }
3223         return NULL; /* Only if marker==NULL */
3224 }
3225
3226
3227 /*******************************/
3228 /* Character utility functions */
3229 /*******************************/
3230
3231 int text_check_bracket(const char ch)
3232 {
3233         int a;
3234         char opens[] = "([{";
3235         char close[] = ")]}";
3236
3237         for (a=0; a<(sizeof(opens)-1); a++) {
3238                 if (ch==opens[a])
3239                         return a+1;
3240                 else if (ch==close[a])
3241                         return -(a+1);
3242         }
3243         return 0;
3244 }
3245
3246 /* TODO, have a function for operators - http://docs.python.org/py3k/reference/lexical_analysis.html#operators */
3247 int text_check_delim(const char ch)
3248 {
3249         int a;
3250         char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,@";
3251
3252         for (a=0; a<(sizeof(delims)-1); a++) {
3253                 if (ch==delims[a])
3254                         return 1;
3255         }
3256         return 0;
3257 }
3258
3259 int text_check_digit(const char ch)
3260 {
3261         if (ch < '0') return 0;
3262         if (ch <= '9') return 1;
3263         return 0;
3264 }
3265
3266 int text_check_identifier(const char ch)
3267 {
3268         if (ch < '0') return 0;
3269         if (ch <= '9') return 1;
3270         if (ch < 'A') return 0;
3271         if (ch <= 'Z' || ch == '_') return 1;
3272         if (ch < 'a') return 0;
3273         if (ch <= 'z') return 1;
3274         return 0;
3275 }
3276
3277 int text_check_whitespace(const char ch)
3278 {
3279         if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
3280                 return 1;
3281         return 0;
3282 }