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