NLA SoC: Start of 'Meta' Strips
[blender.git] / source / blender / blenkernel / intern / text.c
index 67beb4e4397737fb7ae9dc4501c4e7fdd2501d31..8e3d59bbc5869985a5ac92170d3fd27562640bfc 100644 (file)
 #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>
@@ -64,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
@@ -83,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
@@ -119,11 +124,9 @@ static void txt_pop_last(Text *text);
 static void txt_undo_add_op(Text *text, int op);
 static void txt_undo_add_block(Text *text, int op, char *buf);
 static void txt_delete_line(Text *text, TextLine *line);
-static int txt_word_boundary(char ch);
 
 /***/
 
-static char *txt_cut_buffer= NULL;
 static unsigned char undoing;
 
 /* allow to switch off undoing externally */
@@ -148,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) 
@@ -169,9 +175,10 @@ 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");
@@ -246,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_ISTMP; 
-       
        fseek(fp, 0L, SEEK_END);
        len= ftell(fp);
        fseek(fp, 0L, SEEK_SET);        
@@ -311,7 +316,7 @@ 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, res;
@@ -323,8 +328,8 @@ Text *add_text(char *file)
        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");
@@ -334,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);
@@ -418,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;
@@ -561,18 +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
 }
 
-static int txt_word_boundary (char ch)
+/* 0:whitespace, 1:punct, 2:alphanumeric */
+static short txt_char_type (char ch)
 {
-       if (ch < '0') return TRUE;
-       if (ch <= '9') return FALSE;
-       if (ch < 'A') return TRUE;
-       if (ch <= 'Z') return FALSE;
-       if (ch < 'a') return TRUE;
-       if (ch <= 'z') return FALSE;
-       return TRUE;
+       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;
 }
 
 /****************************/
@@ -712,7 +721,7 @@ void txt_move_right(Text *text, short sel)
 void txt_jump_left(Text *text, short sel)
 {
        TextLine **linep, *oldl;
-       int *charp, oldc, count=-1;
+       int *charp, oldc, count, i;
        unsigned char oldu;
 
        if (!text) return;
@@ -725,14 +734,16 @@ void txt_jump_left(Text *text, short sel)
        oldu= undoing;
        undoing= 1; /* Don't push individual moves to undo stack */
 
-       do {
-               txt_move_left(text, sel);
-               count++;
-       } while (*charp>0 && *charp<(*linep)->len && txt_word_boundary((*linep)->line[*charp-1]));
-       if (!count) {
-               while (*charp>0 && *charp<(*linep)->len && !txt_word_boundary((*linep)->line[*charp-1]))
-                       txt_move_left(text, sel);
+       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);
@@ -741,7 +752,7 @@ void txt_jump_left(Text *text, short sel)
 void txt_jump_right(Text *text, short sel)
 {
        TextLine **linep, *oldl;
-       int *charp, oldc;
+       int *charp, oldc, count, i;
        unsigned char oldu;
 
        if (!text) return;
@@ -754,12 +765,16 @@ void txt_jump_right(Text *text, short sel)
        oldu= undoing;
        undoing= 1; /* Don't push individual moves to undo stack */
 
-       do {
-               txt_move_right(text, sel);
-       } while (*charp>0 && *charp<(*linep)->len && !txt_word_boundary((*linep)->line[*charp]));
-       while (*charp>0 && *charp<(*linep)->len && txt_word_boundary((*linep)->line[*charp])) {
-               txt_move_right(text, sel);
+       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);
@@ -948,7 +963,9 @@ int txt_has_sel(Text *text)
 static void txt_delete_sel (Text *text)
 {
        TextLine *tmpl;
+       TextMarker *mrk;
        char *buf;
+       int move, lineno;
        
        if (!text) return;
        if (!text->curl) return;
@@ -966,6 +983,27 @@ static void txt_delete_sel (Text *text)
 
        buf= MEM_mallocN(text->curc+(text->sell->len - text->selc)+1, "textline_string");
        
+       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;
@@ -1009,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;
@@ -1118,15 +1151,6 @@ int txt_find_string(Text *text, char *findstr, int wrap)
                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;
@@ -1204,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;
@@ -1333,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;
@@ -1510,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;
@@ -1523,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;
@@ -1557,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;
 
@@ -1600,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;
@@ -1821,7 +1764,7 @@ 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;
@@ -2034,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;
@@ -2050,12 +1993,29 @@ void txt_do_redo(Text *text)
 void txt_split_curline (Text *text) 
 {
        TextLine *ins;
+       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 */
 
@@ -2094,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);
@@ -2111,10 +2085,25 @@ static void txt_delete_line (Text *text, TextLine *line)
 static void txt_combine_lines (Text *text, TextLine *linea, TextLine *lineb)
 {
        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");
        
@@ -2123,7 +2112,7 @@ static void txt_combine_lines (Text *text, TextLine *linea, TextLine *lineb)
 
        make_new_line(linea, tmp);
        
-       txt_delete_line(text, lineb); 
+       txt_delete_line(text, lineb);
        
        txt_make_dirty(text);
        txt_clean_text(text);
@@ -2137,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) {
@@ -2147,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) {
@@ -2178,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;
@@ -2189,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);
@@ -2218,8 +2245,9 @@ void txt_backspace_word (Text *text)
 
 int txt_add_char (Text *text, char add) 
 {
-       int len;
+       int len, lineno;
        char *tmp;
+       TextMarker *mrk;
        
        if (!text) return 0;
        if (!text->curl) return 0;
@@ -2231,6 +2259,16 @@ 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");
        
        if(text->curc) memcpy(tmp, text->curl->line, text->curc);
@@ -2252,6 +2290,12 @@ 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;
@@ -2260,8 +2304,12 @@ int txt_replace_char (Text *text, char add)
        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 || text->sell!=text->curl || text->selc!=text->curc || add=='\n') {
-               return txt_add_char(text, add);
+       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];
@@ -2535,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 */
+}