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