NLA SoC: Start of 'Meta' Strips
[blender.git] / source / blender / blenkernel / intern / text.c
index 53b7bb975a3fac812709970cbbaf7587588dcd3e..8e3d59bbc5869985a5ac92170d3fd27562640bfc 100644 (file)
@@ -30,6 +30,8 @@
  */
 
 #include <string.h> /* strstr */
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include "MEM_guardedalloc.h"
 
 #include "DNA_scene_types.h"
 #include "DNA_text_types.h"
 
-#include "BKE_bad_level_calls.h"
 #include "BKE_utildefines.h"
 #include "BKE_text.h"
 #include "BKE_library.h"
 #include "BKE_global.h"
 #include "BKE_main.h"
 
+#ifndef DISABLE_PYTHON
 #include "BPY_extern.h"
+#endif
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
@@ -62,9 +65,6 @@ A text should relate to a file as follows -
 (Text *)->flags has the following bits
        TXT_ISDIRTY - should always be set if the file in mem. differs from
                                        the file on disk, or if there is no file on disk.
-       TXT_ISTMP - should always be set if the (Text *)->name file has not
-                                       been written before, and attempts to save should result
-                                       in "Save over?"
        TXT_ISMEM - should always be set if the Text has not been mapped to
                                        a file, in which case (Text *)->name may be NULL or garbage.                    
        TXT_ISEXT - should always be set if the Text is not to be written into
@@ -81,12 +81,19 @@ The st->top determines at what line the top of the text is displayed.
 If the user moves the cursor the st containing that cursor should
 be popped ... other st's retain their own top location.
 
-*/ /***************/
-
+Markers
+--
+The mrk->flags define the behaviour and relationships between markers. The
+upper two bytes are used to hold a group ID, the lower two are normal flags. If
+TMARK_EDITALL is set the group ID defines which other markers should be edited.
 
-/****************/ /*
-       Undo
+The mrk->clr field is used to visually group markers where the flags may not
+match. A template system, for example, may allow editing of repeating tokens
+(in one group) but include other marked positions (in another group) all in the
+same template with the same colour.
 
+Undo
+--
 Undo/Redo works by storing
 events in a queue, and a pointer
 to the current position in the
@@ -120,7 +127,6 @@ static void txt_delete_line(Text *text, TextLine *line);
 
 /***/
 
-static char *txt_cut_buffer= NULL;
 static unsigned char undoing;
 
 /* allow to switch off undoing externally */
@@ -145,10 +151,13 @@ void free_text(Text *text)
        }
        
        BLI_freelistN(&text->lines);
+       BLI_freelistN(&text->markers);
 
        if(text->name) MEM_freeN(text->name);
        MEM_freeN(text->undo_buf);
+#ifndef DISABLE_PYTHON
        if (text->compiled) BPY_free_compiled_text(text);
+#endif
 }
 
 Text *add_empty_text(char *name) 
@@ -166,13 +175,14 @@ Text *add_empty_text(char *name)
        ta->undo_buf= MEM_mallocN(ta->undo_len, "undo buf");
                
        ta->nlines=1;
-       ta->flags= TXT_ISDIRTY | TXT_ISTMP | TXT_ISMEM;
+       ta->flags= TXT_ISDIRTY | TXT_ISMEM;
 
        ta->lines.first= ta->lines.last= NULL;
+       ta->markers.first= ta->markers.last= NULL;
 
        tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
        tmp->line= (char*) MEM_mallocN(1, "textline_string");
-       tmp->format= (char*) MEM_mallocN(2, "Syntax_format");
+       tmp->format= NULL;
        
        tmp->line[0]=0;
        tmp->len= 0;
@@ -209,11 +219,12 @@ static void cleanup_textline(TextLine * tl)
 int reopen_text(Text *text)
 {
        FILE *fp;
-       int i, llen, len;
+       int i, llen, len, res;
        unsigned char *buffer;
        TextLine *tmp;
        char sfile[FILE_MAXFILE];
        char str[FILE_MAXDIR+FILE_MAXFILE];
+       struct stat st;
 
        if (!text || !text->name) return 0;
        
@@ -242,8 +253,6 @@ int reopen_text(Text *text)
        text->undo_len= TXT_INIT_UNDO;
        text->undo_buf= MEM_mallocN(text->undo_len, "undo buf");
        
-       text->flags= TXT_ISDIRTY | TXT_ISTMP; 
-       
        fseek(fp, 0L, SEEK_END);
        len= ftell(fp);
        fseek(fp, 0L, SEEK_SET);        
@@ -256,6 +265,9 @@ int reopen_text(Text *text)
        len = fread(buffer, 1, len, fp);
 
        fclose(fp);
+
+       res= stat(str, &st);
+       text->mtime= st.st_mtime;
        
        text->nlines=0;
        i=0;
@@ -264,7 +276,7 @@ int reopen_text(Text *text)
                if (buffer[i]=='\n') {
                        tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
                        tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
-                       tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format");
+                       tmp->format= NULL;
                        
                        if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
                        tmp->line[llen]=0;
@@ -284,7 +296,7 @@ int reopen_text(Text *text)
        if (llen!=0 || text->nlines==0) {
                tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
                tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
-               tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format");
+               tmp->format= NULL;
                
                if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
 
@@ -304,19 +316,20 @@ int reopen_text(Text *text)
        return 1;
 }
 
-Text *add_text(char *file) 
+Text *add_text(char *file, const char *relpath
 {
        FILE *fp;
-       int i, llen, len;
+       int i, llen, len, res;
        unsigned char *buffer;
        TextLine *tmp;
        Text *ta;
        char sfile[FILE_MAXFILE];
        char str[FILE_MAXDIR+FILE_MAXFILE];
+       struct stat st;
 
        BLI_strncpy(str, file, FILE_MAXDIR+FILE_MAXFILE);
-       if (G.scene) /* can be NULL (bg mode) */
-               BLI_convertstringcode(str, G.sce);
+       if (relpath) /* can be NULL (bg mode) */
+               BLI_convertstringcode(str, relpath);
        BLI_split_dirfile_basic(str, NULL, sfile);
        
        fp= fopen(str, "r");
@@ -326,10 +339,8 @@ Text *add_text(char *file)
        ta->id.us= 1;
 
        ta->lines.first= ta->lines.last= NULL;
+       ta->markers.first= ta->markers.last= NULL;
        ta->curl= ta->sell= NULL;
-
-/*     ta->flags= TXT_ISTMP | TXT_ISEXT; */
-       ta->flags= TXT_ISTMP;
        
        fseek(fp, 0L, SEEK_END);
        len= ftell(fp);
@@ -348,6 +359,9 @@ Text *add_text(char *file)
        len = fread(buffer, 1, len, fp);
 
        fclose(fp);
+
+       res= stat(str, &st);
+       ta->mtime= st.st_mtime;
        
        ta->nlines=0;
        i=0;
@@ -356,7 +370,7 @@ Text *add_text(char *file)
                if (buffer[i]=='\n') {
                        tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
                        tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
-                       tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format");
+                       tmp->format= NULL;
                        
                        if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
                        tmp->line[llen]=0;
@@ -376,7 +390,7 @@ Text *add_text(char *file)
        if (llen!=0 || ta->nlines==0) {
                tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
                tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
-               tmp->format= (char*) MEM_mallocN(llen+2, "Syntax_format");
+               tmp->format= NULL;
                
                if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
 
@@ -407,9 +421,10 @@ Text *copy_text(Text *ta)
        tan->name= MEM_mallocN(strlen(ta->name)+1, "text_name");
        strcpy(tan->name, ta->name);
        
-       tan->flags = ta->flags | TXT_ISDIRTY | TXT_ISTMP;
+       tan->flags = ta->flags | TXT_ISDIRTY;
        
        tan->lines.first= tan->lines.last= NULL;
+       tan->markers.first= tan->markers.last= NULL;
        tan->curl= tan->sell= NULL;
        
        tan->nlines= ta->nlines;
@@ -419,7 +434,7 @@ Text *copy_text(Text *ta)
        while (line) {
                tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
                tmp->line= MEM_mallocN(line->len+1, "textline_string");
-               tmp->format= MEM_mallocN(line->len+2, "Syntax_format");
+               tmp->format= NULL;
                
                strcpy(tmp->line, line->line);
 
@@ -440,14 +455,14 @@ Text *copy_text(Text *ta)
 /* Editing utility functions */
 /*****************************/
 
-static void make_new_line (TextLine *line, char *newline, char *newformat
+static void make_new_line (TextLine *line, char *newline) 
 {
        if (line->line) MEM_freeN(line->line);
        if (line->format) MEM_freeN(line->format);
        
        line->line= newline;
        line->len= strlen(newline);
-       line->format= newformat;
+       line->format= NULL;
 }
 
 static TextLine *txt_new_line(char *str)
@@ -458,7 +473,7 @@ static TextLine *txt_new_line(char *str)
        
        tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
        tmp->line= MEM_mallocN(strlen(str)+1, "textline_string");
-       tmp->format= MEM_mallocN(strlen(str)+2, "Syntax_format");
+       tmp->format= NULL;
        
        strcpy(tmp->line, str);
        
@@ -476,7 +491,7 @@ static TextLine *txt_new_linen(char *str, int n)
        
        tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
        tmp->line= MEM_mallocN(n+1, "textline_string");
-       tmp->format= MEM_mallocN(n+2, "Syntax_format");
+       tmp->format= NULL;
        
        BLI_strncpy(tmp->line, str, n+1);
        
@@ -550,7 +565,23 @@ int txt_get_span (TextLine *from, TextLine *to)
 static void txt_make_dirty (Text *text)
 {
        text->flags |= TXT_ISDIRTY;
+#ifndef DISABLE_PYTHON
        if (text->compiled) BPY_free_compiled_text(text);
+#endif
+}
+
+/* 0:whitespace, 1:punct, 2:alphanumeric */
+static short txt_char_type (char ch)
+{
+       if (ch <= ' ') return 0; /* 32 */
+       if (ch <= '/') return 1; /* 47 */
+       if (ch <= '9') return 2; /* 57 */
+       if (ch <= '@') return 1; /* 64 */
+       if (ch <= 'Z') return 2; /* 90 */
+       if (ch == '_') return 2; /* 95, dont delimit '_' */
+       if (ch <= '`') return 1; /* 96 */
+       if (ch <= 'z') return 2; /* 122 */
+       return 1;
 }
 
 /****************************/
@@ -606,8 +637,7 @@ void txt_move_up(Text *text, short sel)
                        if(!undoing) txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP);
                }
        } else {
-               *charp= 0;
-               if(!undoing) txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP);
+               txt_move_bol(text, sel);
        }
 
        if(!sel) txt_pop_sel(text);
@@ -632,8 +662,7 @@ void txt_move_down(Text *text, short sel)
                } else
                        if(!undoing) txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN);  
        } else {
-               *charp= (*linep)->len;
-               if(!undoing) txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN);
+               txt_move_eol(text, sel);
        }
 
        if(!sel) txt_pop_sel(text);
@@ -689,6 +718,68 @@ void txt_move_right(Text *text, short sel)
        if(!sel) txt_pop_sel(text);
 }
 
+void txt_jump_left(Text *text, short sel)
+{
+       TextLine **linep, *oldl;
+       int *charp, oldc, count, i;
+       unsigned char oldu;
+
+       if (!text) return;
+       if(sel) txt_curs_sel(text, &linep, &charp);
+       else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
+       if (!*linep) return;
+
+       oldl= *linep;
+       oldc= *charp;
+       oldu= undoing;
+       undoing= 1; /* Don't push individual moves to undo stack */
+
+       count= 0;
+       for (i=0; i<3; i++) {
+               if (count < 2) {
+                       while (*charp>0 && txt_char_type((*linep)->line[*charp-1])==i) {
+                               txt_move_left(text, sel);
+                               count++;
+                       }
+               }
+       }
+       if (count==0) txt_move_left(text, sel);
+
+       undoing= oldu;
+       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);
+}
+
+void txt_jump_right(Text *text, short sel)
+{
+       TextLine **linep, *oldl;
+       int *charp, oldc, count, i;
+       unsigned char oldu;
+
+       if (!text) return;
+       if(sel) txt_curs_sel(text, &linep, &charp);
+       else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
+       if (!*linep) return;
+
+       oldl= *linep;
+       oldc= *charp;
+       oldu= undoing;
+       undoing= 1; /* Don't push individual moves to undo stack */
+
+       count= 0;
+       for (i=0; i<3; i++) {
+               if (count < 2) {
+                       while (*charp<(*linep)->len && txt_char_type((*linep)->line[*charp])==i) {
+                               txt_move_right(text, sel);
+                               count++;
+                       }
+               }
+       }
+       if (count==0) txt_move_right(text, sel);
+
+       undoing= oldu;
+       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);
+}
+
 void txt_move_bol (Text *text, short sel) 
 {
        TextLine **linep;
@@ -760,6 +851,11 @@ void txt_move_eof (Text *text, short sel)
 }
 
 void txt_move_toline (Text *text, unsigned int line, short sel)
+{
+       txt_move_to(text, line, 0, sel);
+}
+
+void txt_move_to (Text *text, unsigned int line, unsigned int ch, short sel)
 {
        TextLine **linep, *oldl;
        int *charp, oldc;
@@ -777,10 +873,12 @@ void txt_move_toline (Text *text, unsigned int line, short sel)
                if ((*linep)->next) *linep= (*linep)->next;
                else break;
        }
-       *charp= 0;
+       if (ch>(*linep)->len)
+               ch= (*linep)->len;
+       *charp= ch;
        
        if(!sel) txt_pop_sel(text);
-       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);      
+       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);
 }
 
 /****************************/
@@ -865,7 +963,9 @@ int txt_has_sel(Text *text)
 static void txt_delete_sel (Text *text)
 {
        TextLine *tmpl;
-       char *buf, *format;
+       TextMarker *mrk;
+       char *buf;
+       int move, lineno;
        
        if (!text) return;
        if (!text->curl) return;
@@ -882,13 +982,33 @@ static void txt_delete_sel (Text *text)
        }
 
        buf= MEM_mallocN(text->curc+(text->sell->len - text->selc)+1, "textline_string");
-       format= MEM_mallocN(text->curc+(text->sell->len - text->selc)+2, "Syntax_format");
        
+       if (text->curl != text->sell) {
+               txt_clear_marker_region(text, text->curl, text->curc, text->curl->len, 0, 0);
+               move= txt_get_span(text->curl, text->sell);
+       } else {
+               mrk= txt_find_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
+               if (mrk && (mrk->start > text->curc || mrk->end < text->selc))
+                       txt_clear_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
+               move= 0;
+       }
+
+       mrk= txt_find_marker_region(text, text->sell, text->selc-1, text->sell->len, 0, 0);
+       if (mrk) {
+               lineno= mrk->lineno;
+               do {
+                       mrk->lineno -= move;
+                       if (mrk->start > text->curc) mrk->start -= text->selc - text->curc;
+                       mrk->end -= text->selc - text->curc;
+                       mrk= mrk->next;
+               } while (mrk && mrk->lineno==lineno);
+       }
+
        strncpy(buf, text->curl->line, text->curc);
        strcpy(buf+text->curc, text->sell->line + text->selc);
        buf[text->curc+(text->sell->len - text->selc)]=0;
 
-       make_new_line(text->curl, buf, format);
+       make_new_line(text->curl, buf);
        
        tmpl= text->sell;
        while (tmpl != text->curl) {
@@ -927,11 +1047,6 @@ void txt_sel_line (Text *text)
 /* Cut and paste functions */
 /***************************/
 
-void txt_print_cutbuffer (void) 
-{
-       printf ("Cut buffer\n--\n%s\n--\n", txt_cut_buffer);    
-}
-
 char *txt_to_buf (Text *text)
 {
        int length;
@@ -995,22 +1110,31 @@ char *txt_to_buf (Text *text)
        return buf;
 }
 
-int txt_find_string(Text *text, char *findstr)
+int txt_find_string(Text *text, char *findstr, int wrap)
 {
        TextLine *tl, *startl;
        char *s= NULL;
+       int oldcl, oldsl, oldcc, oldsc;
 
        if (!text || !text->curl || !text->sell) return 0;
        
        txt_order_cursors(text);
 
+       oldcl= txt_get_span(text->lines.first, text->curl);
+       oldsl= txt_get_span(text->lines.first, text->sell);
        tl= startl= text->sell;
+       oldcc= text->curc;
+       oldsc= text->selc;
        
        s= strstr(&tl->line[text->selc], findstr);
        while (!s) {
                tl= tl->next;
-               if (!tl)
-                       tl= text->lines.first;
+               if (!tl) {
+                       if (wrap)
+                               tl= text->lines.first;
+                       else
+                               break;
+               }
 
                s= strstr(tl->line, findstr);
                if (tl==startl)
@@ -1018,24 +1142,15 @@ int txt_find_string(Text *text, char *findstr)
        }
        
        if (s) {
-               text->curl= text->sell= tl;
-               text->curc= (int) (s-tl->line);
-               text->selc= text->curc + strlen(findstr);
-               
+               int newl= txt_get_span(text->lines.first, tl);
+               int newc= (int)(s-tl->line);
+               txt_move_to(text, newl, newc, 0);
+               txt_move_to(text, newl, newc + strlen(findstr), 1);
                return 1;                               
        } else
                return 0;
 }
 
-void txt_cut_sel (Text *text)
-{
-       if (!G.background) /* Python uses txt_cut_sel, which it should not, working around for now  */
-               txt_copy_clipboard(text);
-       
-       txt_delete_sel(text);
-       txt_make_dirty(text);
-}
-
 char *txt_sel_to_buf (Text *text)
 {
        char *buf;
@@ -1113,85 +1228,6 @@ char *txt_sel_to_buf (Text *text)
        return buf;
 }
 
-void txt_copy_sel (Text *text)
-{
-       int length=0;
-       TextLine *tmp, *linef, *linel;
-       int charf, charl;
-       
-       if (!text) return;
-       if (!text->curl) return;
-       if (!text->sell) return;
-
-       if (!txt_has_sel(text)) return;
-       
-       if (txt_cut_buffer) MEM_freeN(txt_cut_buffer);
-       txt_cut_buffer= NULL;
-       
-       if (text->curl==text->sell) {
-               linef= linel= text->curl;
-               
-               if (text->curc < text->selc) {
-                       charf= text->curc;
-                       charl= text->selc;
-               } else{
-                       charf= text->selc;
-                       charl= text->curc;
-               }
-       } else if (txt_get_span(text->curl, text->sell)<0) {
-               linef= text->sell;
-               linel= text->curl;
-
-               charf= text->selc;              
-               charl= text->curc;
-       } else {
-               linef= text->curl;
-               linel= text->sell;
-               
-               charf= text->curc;
-               charl= text->selc;
-       }
-
-       if (linef == linel) {
-               length= charl-charf;
-
-               txt_cut_buffer= MEM_mallocN(length+1, "cut buffera");
-               
-               BLI_strncpy(txt_cut_buffer, linef->line + charf, length+1);
-       } else {
-               length+= linef->len - charf;
-               length+= charl;
-               length++; /* For the '\n' */
-               
-               tmp= linef->next;
-               while (tmp && tmp!= linel) {
-                       length+= tmp->len+1;
-                       tmp= tmp->next;
-               }
-               
-               txt_cut_buffer= MEM_mallocN(length+1, "cut bufferb");
-               
-               strncpy(txt_cut_buffer, linef->line+ charf, linef->len-charf);
-               length= linef->len-charf;
-               
-               txt_cut_buffer[length++]='\n';
-               
-               tmp= linef->next;
-               while (tmp && tmp!=linel) {
-                       strncpy(txt_cut_buffer+length, tmp->line, tmp->len);
-                       length+= tmp->len;
-                       
-                       txt_cut_buffer[length++]='\n';                  
-                       
-                       tmp= tmp->next;
-               }
-               strncpy(txt_cut_buffer+length, linel->line, charl);
-               length+= charl;
-               
-               txt_cut_buffer[length]=0;
-       }
-}
-
 void txt_insert_buf(Text *text, char *in_buffer)
 {
        int i=0, l=0, j, u, len;
@@ -1242,38 +1278,32 @@ void txt_insert_buf(Text *text, char *in_buffer)
        undoing= u;
 }
 
-void txt_free_cut_buffer(void) 
-{
-       if (txt_cut_buffer) MEM_freeN(txt_cut_buffer);
-}
-
-void txt_paste(Text *text)
-{
-       txt_insert_buf(text, txt_cut_buffer);
-}
-
 /******************/
 /* Undo functions */
 /******************/
 
-#define MAX_UNDO_TEST(x) \
-       while (text->undo_pos+x >= text->undo_len) { \
-               if(text->undo_len*2 > TXT_MAX_UNDO) { \
-                       error("Undo limit reached, buffer cleared\n"); \
-                       MEM_freeN(text->undo_buf); \
-                       text->undo_len= TXT_INIT_UNDO; \
-                       text->undo_buf= MEM_mallocN(text->undo_len, "undo buf"); \
-                       text->undo_pos=-1; \
-                       return; \
-               } else { \
-                       void *tmp= text->undo_buf; \
-                       text->undo_buf= MEM_callocN(text->undo_len*2, "undo buf"); \
-                       memcpy(text->undo_buf, tmp, text->undo_len); \
-                       text->undo_len*=2; \
-                       MEM_freeN(tmp); \
-               } \
+static int max_undo_test(Text *text, int x)
+{
+       while (text->undo_pos+x >= text->undo_len) {
+               if(text->undo_len*2 > TXT_MAX_UNDO) {
+                       /* XXX error("Undo limit reached, buffer cleared\n"); */
+                       MEM_freeN(text->undo_buf);
+                       text->undo_len= TXT_INIT_UNDO;
+                       text->undo_buf= MEM_mallocN(text->undo_len, "undo buf");
+                       text->undo_pos=-1;
+                       return 0;
+               } else {
+                       void *tmp= text->undo_buf;
+                       text->undo_buf= MEM_callocN(text->undo_len*2, "undo buf");
+                       memcpy(text->undo_buf, tmp, text->undo_len);
+                       text->undo_len*=2;
+                       MEM_freeN(tmp);
+               }
        }
 
+       return 1;
+}
+
 static void dump_buffer(Text *text) 
 {
        int i= 0;
@@ -1419,7 +1449,8 @@ void txt_print_undo(Text *text)
 
 static void txt_undo_add_op(Text *text, int op)
 {
-       MAX_UNDO_TEST(2);
+       if(!max_undo_test(text, 2))
+               return;
        
        text->undo_pos++;
        text->undo_buf[text->undo_pos]= op;
@@ -1432,7 +1463,8 @@ static void txt_undo_add_block(Text *text, int op, char *buf)
        
        length= strlen(buf);
        
-       MAX_UNDO_TEST(length+11);
+       if(!max_undo_test(text, length+11))
+               return;
 
        text->undo_pos++;
        text->undo_buf[text->undo_pos]= op;
@@ -1466,7 +1498,8 @@ static void txt_undo_add_block(Text *text, int op, char *buf)
 
 void txt_undo_add_toop(Text *text, int op, unsigned int froml, unsigned short fromc, unsigned int tol, unsigned short toc)
 {
-       MAX_UNDO_TEST(15);
+       if(!max_undo_test(text, 15))
+               return;
 
        if (froml==tol && fromc==toc) return;
 
@@ -1509,7 +1542,8 @@ void txt_undo_add_toop(Text *text, int op, unsigned int froml, unsigned short fr
 
 static void txt_undo_add_charop(Text *text, int op, char c)
 {
-       MAX_UNDO_TEST(4);
+       if(!max_undo_test(text, 4))
+               return;
 
        text->undo_pos++;
        text->undo_buf[text->undo_pos]= op;
@@ -1621,7 +1655,6 @@ void txt_do_undo(Text *text)
 
                case UNDO_SWAP:
                        txt_curs_swap(text);
-                       txt_do_undo(text); /* swaps should appear transparent */
                        break;
 
                case UNDO_DBLOCK:
@@ -1731,11 +1764,24 @@ void txt_do_undo(Text *text)
                        text->undo_pos--;
                        break;
                default:
-                       error("Undo buffer error - resetting");
+                       //XXX error("Undo buffer error - resetting");
                        text->undo_pos= -1;
                        
                        break;
        }
+
+       /* next undo step may need evaluating */
+       if (text->undo_pos>=0) {
+               switch (text->undo_buf[text->undo_pos]) {
+                       case UNDO_STO:
+                               txt_do_undo(text);
+                               txt_do_redo(text); /* selections need restoring */
+                               break;
+                       case UNDO_SWAP:
+                               txt_do_undo(text); /* swaps should appear transparent */
+                               break;
+               }
+       }
        
        undoing= 0;     
 }
@@ -1810,7 +1856,7 @@ void txt_do_redo(Text *text)
 
                case UNDO_SWAP:
                        txt_curs_swap(text);
-                       txt_do_undo(text); /* swaps should appear transparent a*/
+                       txt_do_redo(text); /* swaps should appear transparent a*/
                        break;
                        
                case UNDO_CTO:
@@ -1931,7 +1977,7 @@ void txt_do_redo(Text *text)
                        }
                        break;
                default:
-                       error("Undo buffer error - resetting");
+                       //XXX error("Undo buffer error - resetting");
                        text->undo_pos= -1;
 
                        break;
@@ -1947,22 +1993,37 @@ void txt_do_redo(Text *text)
 void txt_split_curline (Text *text) 
 {
        TextLine *ins;
-       char *left, *right, *fleft, *fright;
+       TextMarker *mrk;
+       char *left, *right;
+       int lineno= -1;
        
        if (!text) return;
        if (!text->curl) return;
 
-       txt_delete_sel(text);   
+       txt_delete_sel(text);
+
+       /* Move markers */
+
+       lineno= txt_get_span(text->lines.first, text->curl);
+       mrk= text->markers.first;
+       while (mrk) {
+               if (mrk->lineno==lineno && mrk->start>text->curc) {
+                       mrk->lineno++;
+                       mrk->start -= text->curc;
+                       mrk->end -= text->curc;
+               } else if (mrk->lineno > lineno) {
+                       mrk->lineno++;
+               }
+               mrk= mrk->next;
+       }
 
        /* Make the two half strings */
 
        left= MEM_mallocN(text->curc+1, "textline_string");
-       fleft= MEM_mallocN(text->curc+2, "Syntax_format");
        if (text->curc) memcpy(left, text->curl->line, text->curc);
        left[text->curc]=0;
        
        right= MEM_mallocN(text->curl->len - text->curc+1, "textline_string");
-       fright= MEM_mallocN(text->curl->len - text->curc+2, "Syntax_format");
        if (text->curl->len - text->curc) memcpy(right, text->curl->line+text->curc, text->curl->len-text->curc);
        right[text->curl->len - text->curc]=0;
 
@@ -1973,11 +2034,11 @@ void txt_split_curline (Text *text)
        
        ins= MEM_mallocN(sizeof(TextLine), "textline");
        ins->line= left;
-       ins->format= fleft;
+       ins->format= NULL;
        ins->len= text->curc;
        
        text->curl->line= right;
-       text->curl->format= fright;
+       text->curl->format= NULL;
        text->curl->len= text->curl->len - text->curc;
        
        BLI_insertlinkbefore(&text->lines, text->curl, ins);    
@@ -1993,9 +2054,23 @@ void txt_split_curline (Text *text)
 
 static void txt_delete_line (Text *text, TextLine *line) 
 {
+       TextMarker *mrk=NULL, *nxt;
+       int lineno= -1;
+
        if (!text) return;
        if (!text->curl) return;
 
+       lineno= txt_get_span(text->lines.first, line);
+       mrk= text->markers.first;
+       while (mrk) {
+               nxt= mrk->next;
+               if (mrk->lineno==lineno)
+                       BLI_freelinkN(&text->markers, mrk);
+               else if (mrk->lineno > lineno)
+                       mrk->lineno--;
+               mrk= nxt;
+       }
+
        BLI_remlink (&text->lines, line);
        
        if (line->line) MEM_freeN(line->line);
@@ -2009,21 +2084,35 @@ static void txt_delete_line (Text *text, TextLine *line)
 
 static void txt_combine_lines (Text *text, TextLine *linea, TextLine *lineb)
 {
-       char *tmp, *format;
+       char *tmp;
+       TextMarker *mrk= NULL;
+       int lineno=-1;
        
        if (!text) return;
        
        if(!linea || !lineb) return;
+
+       mrk= txt_find_marker_region(text, lineb, 0, lineb->len, 0, 0);
+       if (mrk) {
+               lineno= mrk->lineno;
+               do {
+                       mrk->lineno--;
+                       mrk->start += linea->len;
+                       mrk->end += linea->len;
+                       mrk= mrk->next;
+               } while (mrk && mrk->lineno==lineno);
+       }
+       if (lineno==-1) lineno= txt_get_span(text->lines.first, lineb);
+       if (!mrk) mrk= text->markers.first;
        
        tmp= MEM_mallocN(linea->len+lineb->len+1, "textline_string");
-       format= MEM_mallocN(linea->len+lineb->len+1, "Syntax_format");
        
        strcpy(tmp, linea->line);
        strcat(tmp, lineb->line);
 
-       make_new_line(linea, tmp, format);
+       make_new_line(linea, tmp);
        
-       txt_delete_line(text, lineb); 
+       txt_delete_line(text, lineb);
        
        txt_make_dirty(text);
        txt_clean_text(text);
@@ -2037,8 +2126,9 @@ void txt_delete_char (Text *text)
        if (!text->curl) return;
 
        if (txt_has_sel(text)) { /* deleting a selection */
-         txt_delete_sel(text);
-         return;
+               txt_delete_sel(text);
+               txt_make_dirty(text);
+               return;
        }
        else if (text->curc== text->curl->len) { /* Appending two lines */
                if (text->curl->next) {
@@ -2047,6 +2137,24 @@ void txt_delete_char (Text *text)
                }
        } else { /* Just deleting a char */
                int i= text->curc;
+
+               TextMarker *mrk= txt_find_marker_region(text, text->curl, i-1, text->curl->len, 0, 0);
+               if (mrk) {
+                       int lineno= mrk->lineno;
+                       if (mrk->end==i) {
+                               if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
+                                       txt_clear_markers(text, mrk->group, TMARK_TEMP);
+                               } else {
+                                       BLI_freelinkN(&text->markers, mrk);
+                               }
+                               return;
+                       }
+                       do {
+                               if (mrk->start>i) mrk->start--;
+                               mrk->end--;
+                               mrk= mrk->next;
+                       } while (mrk && mrk->lineno==lineno);
+               }
                
                c= text->curl->line[i];
                while(i< text->curl->len) {
@@ -2064,6 +2172,12 @@ void txt_delete_char (Text *text)
        if(!undoing) txt_undo_add_charop(text, UNDO_DEL, c);
 }
 
+void txt_delete_word (Text *text) 
+{
+       txt_jump_right(text, 1);
+       txt_delete_sel(text);
+}
+
 void txt_backspace_char (Text *text) 
 {
        char c='\n';
@@ -2072,8 +2186,9 @@ void txt_backspace_char (Text *text)
        if (!text->curl) return;
        
        if (txt_has_sel(text)) { /* deleting a selection */
-         txt_delete_sel(text);
-         return;
+               txt_delete_sel(text);
+               txt_make_dirty(text);
+               return;
        }
        else if (text->curc==0) { /* Appending two lines */
                if (!text->curl->prev) return;
@@ -2083,19 +2198,37 @@ void txt_backspace_char (Text *text)
                
                txt_combine_lines(text, text->curl, text->curl->next);
                txt_pop_sel(text);
-       } 
+       }
        else { /* Just backspacing a char */
-         int i= text->curc-1;
-               
-         c= text->curl->line[i];
-         while(i< text->curl->len) {
-           text->curl->line[i]= text->curl->line[i+1];
-           i++;
-         }
-         text->curl->len--;
-         text->curc--;
+               int i= text->curc-1;
+
+               TextMarker *mrk= txt_find_marker_region(text, text->curl, i, text->curl->len, 0, 0);
+               if (mrk) {
+                       int lineno= mrk->lineno;
+                       if (mrk->start==i+1) {
+                               if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
+                                       txt_clear_markers(text, mrk->group, TMARK_TEMP);
+                               } else {
+                                       BLI_freelinkN(&text->markers, mrk);
+                               }
+                               return;
+                       }
+                       do {
+                               if (mrk->start>i) mrk->start--;
+                               mrk->end--;
+                               mrk= mrk->next;
+                       } while (mrk && mrk->lineno==lineno);
+               }
                
-         txt_pop_sel(text);
+               c= text->curl->line[i];
+               while(i< text->curl->len) {
+                       text->curl->line[i]= text->curl->line[i+1];
+                       i++;
+               }
+               text->curl->len--;
+               text->curc--;
+
+               txt_pop_sel(text);
        }
 
        txt_make_dirty(text);
@@ -2104,10 +2237,17 @@ void txt_backspace_char (Text *text)
        if(!undoing) txt_undo_add_charop(text, UNDO_BS, c);
 }
 
+void txt_backspace_word (Text *text) 
+{
+       txt_jump_left(text, 1);
+       txt_delete_sel(text);
+}
+
 int txt_add_char (Text *text, char add) 
 {
-       int len;
-       char *tmp, *format;
+       int len, lineno;
+       char *tmp;
+       TextMarker *mrk;
        
        if (!text) return 0;
        if (!text->curl) return 0;
@@ -2119,8 +2259,17 @@ int txt_add_char (Text *text, char add)
        
        txt_delete_sel(text);
        
+       mrk= txt_find_marker_region(text, text->curl, text->curc-1, text->curl->len, 0, 0);
+       if (mrk) {
+               lineno= mrk->lineno;
+               do {
+                       if (mrk->start>text->curc) mrk->start++;
+                       mrk->end++;
+                       mrk= mrk->next;
+               } while (mrk && mrk->lineno==lineno);
+       }
+       
        tmp= MEM_mallocN(text->curl->len+2, "textline_string");
-       format= MEM_mallocN(text->curl->len+4, "Syntax_format");
        
        if(text->curc) memcpy(tmp, text->curl->line, text->curc);
        tmp[text->curc]= add;
@@ -2128,7 +2277,7 @@ int txt_add_char (Text *text, char add)
        len= text->curl->len - text->curc;
        if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
        tmp[text->curl->len+1]=0;
-       make_new_line(text->curl, tmp, format);
+       make_new_line(text->curl, tmp);
                
        text->curc++;
 
@@ -2141,10 +2290,48 @@ int txt_add_char (Text *text, char add)
        return 1;
 }
 
+void txt_delete_selected(Text *text)
+{
+       txt_delete_sel(text);
+       txt_make_dirty(text);
+}
+
+int txt_replace_char (Text *text, char add)
+{
+       char del;
+       
+       if (!text) return 0;
+       if (!text->curl) return 0;
+
+       /* If text is selected or we're at the end of the line just use txt_add_char */
+       if (text->curc==text->curl->len || txt_has_sel(text) || add=='\n') {
+               TextMarker *mrk;
+               int i= txt_add_char(text, add);
+               mrk= txt_find_marker(text, text->curl, text->curc, 0, 0);
+               if (mrk && mrk->end==text->curc) mrk->end--;
+               return i;
+       }
+       
+       del= text->curl->line[text->curc];
+       text->curl->line[text->curc]= (unsigned char) add;
+       text->curc++;
+       txt_pop_sel(text);
+       
+       txt_make_dirty(text);
+       txt_clean_text(text);
+
+       /* Should probably create a new op for this */
+       if(!undoing) {
+               txt_undo_add_charop(text, UNDO_DEL, del);
+               txt_undo_add_charop(text, UNDO_INSERT, add);
+       }
+       return 1;
+}
+
 void indent(Text *text)
 {
        int len, num;
-       char *tmp, *format;
+       char *tmp;
        char add = '\t';
        
        if (!text) return;
@@ -2155,7 +2342,6 @@ void indent(Text *text)
        while (TRUE)
        {
                tmp= MEM_mallocN(text->curl->len+2, "textline_string");
-               format= MEM_mallocN(text->curl->len+3, "Syntax_format");
                
                text->curc = 0; 
                if(text->curc) memcpy(tmp, text->curl->line, text->curc);
@@ -2165,7 +2351,7 @@ void indent(Text *text)
                if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
                tmp[text->curl->len+1]=0;
 
-               make_new_line(text->curl, tmp, format);
+               make_new_line(text->curl, tmp);
                        
                text->curc++;
                
@@ -2246,7 +2432,7 @@ void unindent(Text *text)
 void comment(Text *text)
 {
        int len, num;
-       char *tmp, *format;
+       char *tmp;
        char add = '#';
        
        if (!text) return;
@@ -2257,7 +2443,6 @@ void comment(Text *text)
        while (TRUE)
        {
                tmp= MEM_mallocN(text->curl->len+2, "textline_string");
-               format = MEM_mallocN(text->curl->len+3, "Syntax_format");
                
                text->curc = 0; 
                if(text->curc) memcpy(tmp, text->curl->line, text->curc);
@@ -2267,7 +2452,7 @@ void comment(Text *text)
                if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
                tmp[text->curl->len+1]=0;
 
-               make_new_line(text->curl, tmp, format);
+               make_new_line(text->curl, tmp);
                        
                text->curc++;
                
@@ -2398,3 +2583,141 @@ int setcurr_tab (Text *text)
        return i;
 }
 
+/*********************************/
+/* Text marker utility functions */
+/*********************************/
+
+/* Creates and adds a marker to the list maintaining sorted order */
+void txt_add_marker(Text *text, TextLine *line, int start, int end, char color[4], int group, int flags) {
+       TextMarker *tmp, *marker;
+
+       marker= MEM_mallocN(sizeof(TextMarker), "text_marker");
+       
+       marker->lineno= txt_get_span(text->lines.first, line);
+       marker->start= MIN2(start, end);
+       marker->end= MAX2(start, end);
+       marker->group= group;
+       marker->flags= flags;
+
+       marker->color[0]= color[0];
+       marker->color[1]= color[1];
+       marker->color[2]= color[2];
+       marker->color[3]= color[3];
+
+       for (tmp=text->markers.last; tmp; tmp=tmp->prev)
+               if (tmp->lineno < marker->lineno || (tmp->lineno==marker->lineno && tmp->start < marker->start))
+                       break;
+
+       if (tmp) BLI_insertlinkafter(&text->markers, tmp, marker);
+       else BLI_addhead(&text->markers, marker);
+}
+
+/* Returns the first matching marker on the specified line between two points.
+   If the group or flags fields are non-zero the returned flag must be in the
+   specified group and have at least the specified flags set. */
+TextMarker *txt_find_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
+       TextMarker *marker, *next;
+       int lineno= txt_get_span(text->lines.first, line);
+       
+       for (marker=text->markers.first; marker; marker=next) {
+               next= marker->next;
+
+               if (group && marker->group != group) continue;
+               else if ((marker->flags & flags) != flags) continue;
+               else if (marker->lineno < lineno) continue;
+               else if (marker->lineno > lineno) break;
+
+               if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
+                               (marker->start<end && marker->end>start))
+                       return marker;
+       }
+       return NULL;
+}
+
+/* Clears all markers on the specified line between two points. If the group or
+   flags fields are non-zero the returned flag must be in the specified group
+   and have at least the specified flags set. */
+short txt_clear_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
+       TextMarker *marker, *next;
+       int lineno= txt_get_span(text->lines.first, line);
+       short cleared= 0;
+       
+       for (marker=text->markers.first; marker; marker=next) {
+               next= marker->next;
+
+               if (group && marker->group != group) continue;
+               else if ((marker->flags & flags) != flags) continue;
+               else if (marker->lineno < lineno) continue;
+               else if (marker->lineno > lineno) break;
+
+               if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
+                       (marker->start<end && marker->end>start)) {
+                       BLI_freelinkN(&text->markers, marker);
+                       cleared= 1;
+               }
+       }
+       return cleared;
+}
+
+/* Clears all markers in the specified group (if given) with at least the
+   specified flags set. Useful for clearing temporary markers (group=0,
+   flags=TMARK_TEMP) */
+short txt_clear_markers(Text *text, int group, int flags) {
+       TextMarker *marker, *next;
+       short cleared= 0;
+       
+       for (marker=text->markers.first; marker; marker=next) {
+               next= marker->next;
+
+               if ((!group || marker->group==group) &&
+                               (marker->flags & flags) == flags) {
+                       BLI_freelinkN(&text->markers, marker);
+                       cleared= 1;
+               }
+       }
+       return cleared;
+}
+
+/* Finds the marker at the specified line and cursor position with at least the
+   specified flags set in the given group (if non-zero). */
+TextMarker *txt_find_marker(Text *text, TextLine *line, int curs, int group, int flags) {
+       TextMarker *marker;
+       int lineno= txt_get_span(text->lines.first, line);
+       
+       for (marker=text->markers.first; marker; marker=marker->next) {
+               if (group && marker->group != group) continue;
+               else if ((marker->flags & flags) != flags) continue;
+               else if (marker->lineno < lineno) continue;
+               else if (marker->lineno > lineno) break;
+
+               if (marker->start <= curs && curs <= marker->end)
+                       return marker;
+       }
+       return NULL;
+}
+
+/* Finds the previous marker in the same group. If no other is found, the same
+   marker will be returned */
+TextMarker *txt_prev_marker(Text *text, TextMarker *marker) {
+       TextMarker *tmp= marker;
+       while (tmp) {
+               if (tmp->prev) tmp= tmp->prev;
+               else tmp= text->markers.last;
+               if (tmp->group == marker->group)
+                       return tmp;
+       }
+       return NULL; /* Only if marker==NULL */
+}
+
+/* Finds the next marker in the same group. If no other is found, the same
+   marker will be returned */
+TextMarker *txt_next_marker(Text *text, TextMarker *marker) {
+       TextMarker *tmp= marker;
+       while (tmp) {
+               if (tmp->next) tmp= tmp->next;
+               else tmp= text->markers.first;
+               if (tmp->group == marker->group)
+                       return tmp;
+       }
+       return NULL; /* Only if marker==NULL */
+}