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