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