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