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