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