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