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