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