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