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