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