Text space
authorSergey Sharybin <sergey.vfx@gmail.com>
Wed, 13 Oct 2010 06:06:39 +0000 (06:06 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Wed, 13 Oct 2010 06:06:39 +0000 (06:06 +0000)
==========

Main changes:
- lines could be partially shown when they starts somewhere behind the upper
  boundary of area but because of word-wrapping some part of line will be show
- fixed caret navigatiog in area when tabs aren't replaced by spaces
- highlight the whole current line not only it's wrapped segment with caret
- when you're in replace mode cursor would be as long as the tab's width if it's under tab symbol

This fixes:
  #22399: Text Editor: word-wrapped lines prevent navigating through text with up-arrow.
  #21163: Text editor scrollbar problem with word wrap

source/blender/blenloader/intern/readfile.c
source/blender/editors/space_text/space_text.c
source/blender/editors/space_text/text_draw.c
source/blender/editors/space_text/text_intern.h
source/blender/editors/space_text/text_ops.c
source/blender/makesdna/DNA_space_types.h

index 0eb25a6b8941212bee060e8e60dd42541aae0618..22641d51ed954bcfd45c8d9dfdc2085f9625de36 100644 (file)
@@ -4646,6 +4646,7 @@ static void lib_link_screen(FileData *fd, Main *main)
                                                SpaceText *st= (SpaceText *)sl;
 
                                                st->text= newlibadr(fd, sc->id.lib, st->text);
+                                               st->drawcache= NULL;
 
                                        }
                                        else if(sl->spacetype==SPACE_SCRIPT) {
@@ -4877,6 +4878,8 @@ void lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *curscene)
 
                                        st->text= restore_pointer_by_name(newmain, (ID *)st->text, 1);
                                        if(st->text==NULL) st->text= newmain->text.first;
+
+                                       st->drawcache= NULL;
                                }
                                else if(sl->spacetype==SPACE_SCRIPT) {
                                        SpaceScript *scpt= (SpaceScript *)sl;
index 7f7a07f8cf75235ece1ea0279af1a135cb9b09e9..5ee7ca3c3ef5f65496d5069ce5cb611c7bef68f1 100644 (file)
@@ -92,6 +92,7 @@ static void text_free(SpaceLink *sl)
        SpaceText *stext= (SpaceText*) sl;
        
        stext->text= NULL;
+       text_free_caches(stext);
 }
 
 
@@ -104,9 +105,11 @@ static void text_init(struct wmWindowManager *wm, ScrArea *sa)
 static SpaceLink *text_duplicate(SpaceLink *sl)
 {
        SpaceText *stextn= MEM_dupallocN(sl);
-       
+
        /* clear or remove stuff from old */
-       
+
+       stextn->drawcache= NULL; /* space need it's own cache */
+
        return (SpaceLink *)stextn;
 }
 
@@ -132,8 +135,11 @@ static void text_listener(ScrArea *sa, wmNotifier *wmn)
 
                        switch(wmn->action) {
                                case NA_EDITED:
-                                       if(st->text)
+                                       if(st->text) {
+                                               text_drawcache_tag_update(st, 1);
                                                text_update_edited(st->text);
+                                       }
+
                                        ED_area_tag_redraw(sa);
                                        /* no break -- fall down to tag redraw */
                                case NA_ADDED:
index be2d993dab5b3fc06866c86bd54313d893cd513c..c6036eb354bd83037cfb7b74176a6df9a3618acc 100644 (file)
@@ -10,7 +10,6 @@
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
@@ -521,9 +520,22 @@ void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *
        linep= text->lines.first;
        i= st->top;
        while(i>0 && linep) {
-               if(linep == linein) return; /* Line before top */
-               linep= linep->next;
-               i--;
+               int lines= text_get_visible_lines(st, ar, linep->line);
+
+               /* Line before top */
+               if(linep == linein) {
+                       if(lines <= i)
+                               /* no visible part of line */
+                               return;
+               }
+
+               if (i-lines<0) {
+                       break;
+               } else {
+                       linep= linep->next;
+                       (*offl)+= lines-1;
+                       i-= lines;
+               }
        }
 
        max= wrap_width(st, ar);
@@ -548,10 +560,18 @@ void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *
 
                        while(chars--) {
                                if(i-start>=max) {
-                                       if(chop && linep==linein && i >= cursin)
+                                       if(chop && linep==linein && i >= cursin) {
+                                               if (i==cursin) {
+                                                       (*offl)++;
+                                                       *offc -= end-start;
+                                               }
+
                                                return;
+                                       }
+
                                        (*offl)++;
                                        *offc -= end-start;
+
                                        start= end;
                                        end += max;
                                        chop= 1;
@@ -570,7 +590,66 @@ void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *
        }
 }
 
-static int get_char_pos(SpaceText *st, char *line, int cur)
+void wrap_offset_in_line(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
+{
+       int i, j, start, end, chars, max, chop;
+       char ch;
+
+       *offl= *offc= 0;
+
+       if(!st->text) return;
+       if(!st->wordwrap) return;
+
+       max= wrap_width(st, ar);
+
+       start= 0;
+       end= max;
+       chop= 1;
+       chars= 0;
+       *offc= 0;
+
+       for(i=0, j=0; linein->line[j]!='\0'; j++) {
+
+               /* Mimic replacement of tabs */
+               ch= linein->line[j];
+               if(ch=='\t') {
+                       chars= st->tabnumber-i%st->tabnumber;
+                       if(i<cursin) cursin += chars-1;
+                       ch= ' ';
+               }
+               else
+                       chars= 1;
+
+               while(chars--) {
+                       if(i-start>=max) {
+                               if(chop && i >= cursin) {
+                                       if (i==cursin) {
+                                               (*offl)++;
+                                               *offc -= end-start;
+                                       }
+
+                                       return;
+                               }
+
+                               (*offl)++;
+                               *offc -= end-start;
+
+                               start= end;
+                               end += max;
+                               chop= 1;
+                       }
+                       else if(ch==' ' || ch=='-') {
+                               end = i+1;
+                               chop= 0;
+                               if(i >= cursin)
+                                       return;
+                       }
+                       i++;
+               }
+       }
+}
+
+int text_get_char_pos(SpaceText *st, char *line, int cur)
 {
        int a=0, i;
        
@@ -583,7 +662,7 @@ static int get_char_pos(SpaceText *st, char *line, int cur)
        return a;
 }
 
-static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format)
+static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format, int skip)
 {
        FlattenString fs;
        int basex, i, a, len, start, end, max, lines;
@@ -599,6 +678,14 @@ static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char
        end= max;
        for(i=0; i<len; i++) {
                if(i-start >= max) {
+                       /* skip hidden part of line */
+                       if(skip) {
+                               skip--;
+                               start= end;
+                               end += max;
+                               continue;
+                       }
+
                        /* Draw the visible portion of text on the overshot line */
                        for(a=start; a<end; a++) {
                                if(st->showsyntax && format) format_draw_color(format[a]);
@@ -609,6 +696,8 @@ static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char
                        lines++;
                        start= end;
                        end += max;
+
+                       if(y<=0) break;
                }
                else if(str[i]==' ' || str[i]=='-') {
                        end = i+1;
@@ -616,7 +705,7 @@ static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char
        }
 
        /* Draw the remaining text */
-       for(a=start; a<len; a++) {
+       for(a=start; a<len && y > 0; a++) {
                if(st->showsyntax && format)
                        format_draw_color(format[a]);
 
@@ -631,7 +720,7 @@ static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char
 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format)
 {
        FlattenString fs;
-       int r=0, w= 0;
+       int r=0, w= 0, amount;
        int *acc;
        char *in;
 
@@ -647,18 +736,26 @@ static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int dra
 
        if(draw) {
                if(st->showsyntax && format) {
-                       int amount, a;
+                       int a;
                        format = format+cshift;
                
                        amount = strlen(in);
+                       if(maxwidth)
+                               amount= MIN2(amount, maxwidth);
                        
                        for(a = 0; a < amount; a++) {
                                format_draw_color(format[a]);
                                x += text_font_draw_character(st, x, y, in[a]);
                        }
                }
-               else
+               else {
+                       amount = strlen(in);
+                       if(maxwidth)
+                               amount= MIN2(amount, maxwidth);
+
+                       in[amount]= 0;
                        text_font_draw(st, x, y, in);
+               }
        }
        else {
                while(w-- && *acc++ < maxwidth)
@@ -675,18 +772,307 @@ static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int dra
                return r+TXT_OFFSET;
 }
 
+/************************ cache utilities *****************************/
+
+typedef struct DrawCache {
+       int *line_height;
+       int total_lines, nlines;
+
+       /* this is needed to check cache relevance */
+       int winx, wordwrap, showlinenrs, tabnumber;
+       short lheight;
+       char cwidth;
+       char text_id[MAX_ID_NAME];
+
+       /* for partial lines recalculation */
+       short update_flag;
+       int valid_head, valid_tail; /* amount of unchanged lines */
+} DrawCache;
+
+static void text_drawcache_init(SpaceText *st)
+{
+       DrawCache *drawcache= MEM_callocN(sizeof (DrawCache), "text draw cache");
+
+       drawcache->winx= -1;
+       drawcache->nlines= BLI_countlist(&st->text->lines);
+       drawcache->text_id[0]= '\0';
+
+       st->drawcache= drawcache;
+}
+
+static void text_update_drawcache(SpaceText *st, ARegion *ar)
+{
+       DrawCache *drawcache;
+       int full_update= 0, nlines= 0;
+       Text *txt= st->text;
+
+       if(!st->drawcache) text_drawcache_init(st);
+
+       text_update_character_width(st);
+
+       drawcache= (DrawCache *)st->drawcache;
+       nlines= drawcache->nlines;
+
+       /* check if full cache update is needed */
+       full_update|= drawcache->winx != ar->winx;                 /* area was resized */
+       full_update|= drawcache->wordwrap != st->wordwrap;         /* word-wrapping option was toggled */
+       full_update|= drawcache->showlinenrs != st->showlinenrs; /* word-wrapping option was toggled */
+       full_update|= drawcache->tabnumber != st->tabnumber;  /* word-wrapping option was toggled */
+       full_update|= drawcache->lheight != st->lheight;      /* word-wrapping option was toggled */
+       full_update|= drawcache->cwidth != st->cwidth;        /* word-wrapping option was toggled */
+       full_update|= strncmp(drawcache->text_id, txt->id.name, MAX_ID_NAME); /* text datablock was changed */
+
+       if(st->wordwrap) {
+               /* update line heights */
+               if(full_update || !drawcache->line_height) {
+                       drawcache->valid_head  = 0;
+                       drawcache->valid_tail  = 0;
+                       drawcache->update_flag = 1;
+               }
+
+               if(drawcache->update_flag) {
+                       TextLine *line= st->text->lines.first;
+                       int lineno= 0, size, lines_count;
+                       int *fp= drawcache->line_height, *new_tail, *old_tail;
+
+                       nlines= BLI_countlist(&txt->lines);
+                       size= sizeof(int)*nlines;
+
+                       if(fp) fp= MEM_reallocN(fp, size);
+                       else fp= MEM_callocN(size, "text drawcache line_height");
+
+                       drawcache->valid_tail= drawcache->valid_head= 0;
+                       old_tail= fp + drawcache->nlines - drawcache->valid_tail;
+                       new_tail= fp + nlines - drawcache->valid_tail;
+                       memmove(new_tail, old_tail, drawcache->valid_tail);
+
+                       drawcache->total_lines= 0;
+
+                       if(st->showlinenrs)
+                               st->linenrs_tot= (int)floor(log10((float)nlines)) + 1;
+
+                       while(line) {
+                               if(drawcache->valid_head) { /* we're inside valid head lines */
+                                       lines_count= fp[lineno];
+                                       drawcache->valid_head--;
+                               } else if (lineno > new_tail - fp) {  /* we-re inside valid tail lines */
+                                       lines_count= fp[lineno];
+                               } else {
+                                       lines_count= text_get_visible_lines(st, ar, line->line);
+                               }
+
+                               fp[lineno]= lines_count;
+
+                               line= line->next;
+                               lineno++;
+                               drawcache->total_lines+= lines_count;
+                       }
+
+                       drawcache->line_height= fp;
+               }
+       } else {
+               if(drawcache->line_height) {
+                       MEM_freeN(drawcache->line_height);
+                       drawcache->line_height= NULL;
+               }
+
+               if(full_update || drawcache->update_flag) {
+                       nlines= BLI_countlist(&txt->lines);
+
+                       if(st->showlinenrs)
+                               st->linenrs_tot= (int)floor(log10((float)nlines)) + 1;
+               }
+
+               drawcache->total_lines= nlines;
+       }
+
+       drawcache->nlines= nlines;
+
+       /* store settings */
+       drawcache->winx        = ar->winx;
+       drawcache->wordwrap    = st->wordwrap;
+       drawcache->lheight     = st->lheight;
+       drawcache->cwidth      = st->cwidth;
+       drawcache->showlinenrs = st->showlinenrs;
+       drawcache->tabnumber   = st->tabnumber;
+
+       strncpy(drawcache->text_id, txt->id.name, MAX_ID_NAME);
+
+       /* clear update flag */
+       drawcache->update_flag = 0;
+       drawcache->valid_head  = 0;
+       drawcache->valid_tail  = 0;
+}
+
+void text_drawcache_tag_update(SpaceText *st, int full)
+{
+       DrawCache *drawcache= (DrawCache *)st->drawcache;
+
+       if(drawcache) {
+               Text *txt= st->text;
+
+               if(drawcache->update_flag) {
+                       /* happens when tagging update from space listener */
+                       /* should do nothing to prevent locally tagged cache be fully recalculated */
+                       return;
+               }
+
+               if(!full) {
+                       int sellno= BLI_findindex(&txt->lines, txt->sell);
+                       int curlno= BLI_findindex(&txt->lines, txt->curl);
+
+                       if(curlno < sellno) {
+                               drawcache->valid_head= curlno;
+                               drawcache->valid_tail= drawcache->nlines - sellno - 1;
+                       } else {
+                               drawcache->valid_head= sellno;
+                               drawcache->valid_tail= drawcache->nlines - curlno - 1;
+                       }
+
+                       /* quick cache recalculation is also used in delete operator,
+                          which could merge lines which are adjusent to current selection lines
+                          expand recalculate area to this lines */
+                       if(drawcache->valid_head>0) drawcache->valid_head--;
+                       if(drawcache->valid_tail>0) drawcache->valid_tail--;
+               } else {
+                       drawcache->valid_head= 0;
+                       drawcache->valid_tail= 0;
+               }
+
+               drawcache->update_flag= 1;
+       }
+}
+
+void text_free_caches(SpaceText *st)
+{
+       DrawCache *drawcache= (DrawCache *)st->drawcache;
+
+       if(drawcache) {
+               if(drawcache->line_height)
+                       MEM_freeN(drawcache->line_height);
+
+               MEM_freeN(drawcache);
+       }
+}
+
+/************************ word-wrap utilities *****************************/
+
+/* cache should be updated in caller */
+int text_get_visible_lines_no(SpaceText *st, int lineno)
+{
+       DrawCache *drawcache= (DrawCache *)st->drawcache;
+
+       return drawcache->line_height[lineno];
+}
+
+int text_get_visible_lines(SpaceText *st, ARegion *ar, char *str)
+{
+       int i, j, start, end, max, lines, chars;
+       char ch;
+
+       max= wrap_width(st, ar);
+       lines= 1;
+       start= 0;
+       end= max;
+       for(i= 0, j= 0; str[j] != '\0'; j++) {
+               /* Mimic replacement of tabs */
+               ch= str[j];
+               if(ch=='\t') {
+                       chars= st->tabnumber-i%st->tabnumber;
+                       ch= ' ';
+               }
+               else chars= 1;
+
+               while(chars--) {
+                       if(i-start >= max) {
+                               lines++;
+                               start= end;
+                               end += max;
+                       }
+                       else if(ch==' ' || ch=='-') {
+                               end= i+1;
+                       }
+
+                       i++;
+               }
+       }
+
+       return lines;
+}
+
+int text_get_span_wrap(SpaceText *st, ARegion *ar, TextLine *from, TextLine *to)
+{
+       if(st->wordwrap) {
+               int ret=0;
+               TextLine *tmp= from;
+
+               /* Look forwards */
+               while (tmp) {
+                       if (tmp == to) return ret;
+                       ret+= text_get_visible_lines(st, ar, tmp->line);
+                       tmp= tmp->next;
+               }
+
+               return ret;
+       } else return txt_get_span(from, to);
+}
+
+int text_get_total_lines(SpaceText *st, ARegion *ar)
+{
+       DrawCache *drawcache;
+
+       text_update_drawcache(st, ar);
+       drawcache= (DrawCache *)st->drawcache;
+
+       return drawcache->total_lines;
+}
+
+/* Move pointer to first visible line (top) */
+static TextLine *first_visible_line(SpaceText *st, ARegion *ar, int *wrap_top)
+{
+       Text *text= st->text;
+       TextLine* pline= text->lines.first;
+       int i= st->top, lineno= 0;
+       DrawCache *drawcache;
+
+       text_update_drawcache(st, ar);
+       drawcache= (DrawCache *)st->drawcache;
+
+       if(wrap_top) *wrap_top= 0;
+
+       if(st->wordwrap) {
+               while(i>0 && pline) {
+                       int lines= text_get_visible_lines_no(st, lineno);
+
+                       if (i-lines<0) {
+                               if(wrap_top) *wrap_top= i;
+                               break;
+                       } else {
+                               pline= pline->next;
+                               i-= lines;
+                               lineno++;
+                       }
+               }
+       } else {
+               for(i=st->top, pline= text->lines.first; pline->next && i>0; i--)
+                       pline= pline->next;
+       }
+
+       return pline;
+}
+
 /************************ draw scrollbar *****************************/
 
 static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll)
 {
-       int lhlstart, lhlend, ltexth;
+       int lhlstart, lhlend, ltexth, sell_off, curl_off;
        short barheight, barstart, hlstart, hlend, blank_lines;
        short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
 
        pix_top_margin = 8;
        pix_bottom_margin = 4;
        pix_available = ar->winy - pix_top_margin - pix_bottom_margin;
-       ltexth= txt_get_span(st->text->lines.first, st->text->lines.last);
+       ltexth= text_get_total_lines(st, ar);
        blank_lines = st->viewlines / 2;
        
        /* nicer code: use scroll rect for entire bar */
@@ -722,10 +1108,10 @@ static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll)
        st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
        if(st->pix_per_line<.1) st->pix_per_line=.1f;
 
-       lhlstart = MIN2(txt_get_span(st->text->lines.first, st->text->curl), 
-                               txt_get_span(st->text->lines.first, st->text->sell));
-       lhlend = MAX2(txt_get_span(st->text->lines.first, st->text->curl), 
-                               txt_get_span(st->text->lines.first, st->text->sell));
+       curl_off= text_get_span_wrap(st, ar, st->text->lines.first, st->text->curl);
+       sell_off= text_get_span_wrap(st, ar, st->text->lines.first, st->text->sell);
+       lhlstart = MIN2(curl_off, sell_off);
+       lhlend = MAX2(curl_off, sell_off);
 
        if(ltexth > 0) {
                hlstart = (lhlstart * pix_available)/ltexth;
@@ -811,78 +1197,80 @@ static void draw_markers(SpaceText *st, ARegion *ar)
 {
        Text *text= st->text;
        TextMarker *marker, *next;
-       TextLine *top, *bottom, *line;
-       int offl, offc, i, cy, x1, x2, y1, y2, x, y;
+       TextLine *top, *line;
+       int offl, offc, i, x1, x2, y1, y2, x, y;
+       int topi, topy;
+
+       /* Move pointer to first visible line (top) */
+       top= first_visible_line(st, ar, NULL);
+       topi= BLI_findindex(&text->lines, top);
 
-       for(i=st->top, top= text->lines.first; top->next && i>0; i--)
-               top= top->next;
+       topy= txt_get_span(text->lines.first, top);
 
-       for(i=st->viewlines-1, bottom=top; bottom->next && i>0; i--)
-               bottom= bottom->next;
-       
        for(marker= text->markers.first; marker; marker= next) {
                next= marker->next;
 
-               for(cy= 0, line= top; line; cy++, line= line->next) {
-                       if(cy+st->top==marker->lineno) {
-                               /* Remove broken markers */
-                               if(marker->end>line->len || marker->start>marker->end) {
-                                       BLI_freelinkN(&text->markers, marker);
-                                       break;
-                               }
+               /* invisible line (before top) */
+               if(marker->lineno<topi) continue;
 
-                               wrap_offset(st, ar, line, marker->start, &offl, &offc);
-                               x1= get_char_pos(st, line->line, marker->start) - st->left + offc;
-                               y1= cy + offl;
-                               wrap_offset(st, ar, line, marker->end, &offl, &offc);
-                               x2= get_char_pos(st, line->line, marker->end) - st->left + offc;
-                               y2= cy + offl;
-
-                               glColor3ub(marker->color[0], marker->color[1], marker->color[2]);
-                               x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
-                               y= ar->winy-3;
-
-                               if(y1==y2) {
-                                       y -= y1*st->lheight;
-                                       glBegin(GL_LINE_LOOP);
-                                       glVertex2i(x+x2*st->cwidth+1, y);
-                                       glVertex2i(x+x1*st->cwidth-2, y);
-                                       glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
-                                       glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
-                                       glEnd();
-                               }
-                               else {
-                                       y -= y1*st->lheight;
-                                       glBegin(GL_LINE_STRIP);
-                                       glVertex2i(ar->winx, y);
-                                       glVertex2i(x+x1*st->cwidth-2, y);
-                                       glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
-                                       glVertex2i(ar->winx, y-st->lheight);
-                                       glEnd();
-                                       y-=st->lheight;
-
-                                       for(i=y1+1; i<y2; i++) {
-                                               glBegin(GL_LINES);
-                                               glVertex2i(x, y);
-                                               glVertex2i(ar->winx, y);
-                                               glVertex2i(x, y-st->lheight);
-                                               glVertex2i(ar->winx, y-st->lheight);
-                                               glEnd();
-                                               y-=st->lheight;
-                                       }
+               line= BLI_findlink(&text->lines, marker->lineno);
 
-                                       glBegin(GL_LINE_STRIP);
-                                       glVertex2i(x, y);
-                                       glVertex2i(x+x2*st->cwidth+1, y);
-                                       glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
-                                       glVertex2i(x, y-st->lheight);
-                                       glEnd();
-                               }
+               /* Remove broken markers */
+               if(marker->end>line->len || marker->start>marker->end) {
+                       BLI_freelinkN(&text->markers, marker);
+                       continue;
+               }
 
-                               break;
+               wrap_offset(st, ar, line, marker->start, &offl, &offc);
+               y1 = txt_get_span(top, line) - st->top + offl + topy;
+               x1 = text_get_char_pos(st, line->line, marker->start) - st->left + offc;
+
+               wrap_offset(st, ar, line, marker->end, &offl, &offc);
+               y2 = txt_get_span(top, line) - st->top + offl + topy;
+               x2 = text_get_char_pos(st, line->line, marker->end) - st->left + offc;
+
+               /* invisible part of line (before top, after last visible line) */
+               if(y2 < 0 || y1 > st->top+st->viewlines) continue;
+
+               glColor3ub(marker->color[0], marker->color[1], marker->color[2]);
+               x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
+               y= ar->winy-3;
+
+               if(y1==y2) {
+                       y -= y1*st->lheight;
+                       glBegin(GL_LINE_LOOP);
+                       glVertex2i(x+x2*st->cwidth+1, y);
+                       glVertex2i(x+x1*st->cwidth-2, y);
+                       glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
+                       glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
+                       glEnd();
+               }
+               else {
+                       y -= y1*st->lheight;
+                       glBegin(GL_LINE_STRIP);
+                       glVertex2i(ar->winx, y);
+                       glVertex2i(x+x1*st->cwidth-2, y);
+                       glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
+                       glVertex2i(ar->winx, y-st->lheight);
+                       glEnd();
+                       y-=st->lheight;
+
+                       for(i=y1+1; i<y2; i++) {
+                               glBegin(GL_LINES);
+                               glVertex2i(x, y);
+                               glVertex2i(ar->winx, y);
+                               glVertex2i(x, y-st->lheight);
+                               glVertex2i(ar->winx, y-st->lheight);
+                               glEnd();
+                               y-=st->lheight;
                        }
 
-                       if(line==bottom) break;
+                       glBegin(GL_LINE_STRIP);
+                       glVertex2i(x, y);
+                       glVertex2i(x+x2*st->cwidth+1, y);
+                       glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
+                       glVertex2i(x, y-st->lheight);
+                       glEnd();
                }
        }
 }
@@ -1058,16 +1446,16 @@ static void draw_cursor(SpaceText *st, ARegion *ar)
        Text *text= st->text;
        int vcurl, vcurc, vsell, vselc, hidden=0;
        int offl, offc, x, y, w, i;
-       
+
        /* Draw the selection */
        if(text->curl!=text->sell || text->curc!=text->selc) {
                /* Convert all to view space character coordinates */
                wrap_offset(st, ar, text->curl, text->curc, &offl, &offc);
                vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
-               vcurc = get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
+               vcurc = text_get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
                wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
                vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
-               vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
+               vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
 
                if(vcurc<0) vcurc=0;
                if(vselc<0) vselc=0, hidden=1;
@@ -1106,7 +1494,7 @@ static void draw_cursor(SpaceText *st, ARegion *ar)
        else {
                wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
                vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
-               vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
+               vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
 
                if(vselc<0) {
                        vselc= 0;
@@ -1115,17 +1503,30 @@ static void draw_cursor(SpaceText *st, ARegion *ar)
        }
 
        if(st->line_hlight) {
-               y= ar->winy-2 - vsell*st->lheight;
-               if(!(y<0 || y > ar->winy)) { /* check we need to draw */
-                       int x1= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
-                       int x2= x1 + ar->winx;
-                       y= ar->winy-2 - vsell*st->lheight;
-       
+               int x1, x2, y1, y2;
+
+               if(st->wordwrap) {
+                       int visible_lines = text_get_visible_lines(st, ar, text->sell->line);
+                       int offl, offc;
+
+                       wrap_offset_in_line(st, ar, text->sell, text->selc, &offl, &offc);
+
+                       y1= ar->winy-2 - (vsell-offl)*st->lheight;
+                       y2= y1-st->lheight*visible_lines+1;
+               } else {
+                       y1= ar->winy-2 - vsell*st->lheight;
+                       y2= y1-st->lheight+1;
+               }
+
+               if(!(y1<0 || y2 > ar->winy)) { /* check we need to draw */
+                       x1= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
+                       x2= x1 + ar->winx;
+
                        glColor4ub(255, 255, 255, 32);
                        
                        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                        glEnable(GL_BLEND);
-                       glRecti(x1-4, y, x2, y-st->lheight+1);
+                       glRecti(x1-4, y1, x2, y2);
                        glDisable(GL_BLEND);
                }
        }
@@ -1138,8 +1539,10 @@ static void draw_cursor(SpaceText *st, ARegion *ar)
                
                if(st->overwrite) {
                        char ch= text->sell->line[text->selc];
-                       if(!ch) ch= ' ';
+                       
                        w= st->cwidth;
+                       if(ch=='\t')  w*= st->tabnumber-(vselc+st->left)%st->tabnumber;
+                       
                        UI_ThemeColor(TH_HILITE);
                        glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1);
                }
@@ -1243,7 +1646,7 @@ static void draw_brackets(SpaceText *st, ARegion *ar)
        /* draw opening bracket */
        ch= startl->line[startc];
        wrap_offset(st, ar, startl, startc, &offl, &offc);
-       viewc= get_char_pos(st, startl->line, startc) - st->left + offc;
+       viewc= text_get_char_pos(st, startl->line, startc) - st->left + offc;
 
        if(viewc >= 0){
                viewl= txt_get_span(text->lines.first, startl) - st->top + offl;
@@ -1255,7 +1658,7 @@ static void draw_brackets(SpaceText *st, ARegion *ar)
        /* draw closing bracket */
        ch= endl->line[endc];
        wrap_offset(st, ar, endl, endc, &offl, &offc);
-       viewc= get_char_pos(st, endl->line, endc) - st->left + offc;
+       viewc= text_get_char_pos(st, endl->line, endc) - st->left + offc;
 
        if(viewc >= 0) {
                viewl= txt_get_span(text->lines.first, endl) - st->top + offl;
@@ -1273,12 +1676,15 @@ void draw_text_main(SpaceText *st, ARegion *ar)
        TextLine *tmp;
        rcti scroll;
        char linenr[12];
-       int i, x, y, winx, linecount= 0;
+       int i, x, y, winx, linecount= 0, lineno= 0;
+       int wraplinecount= 0, wrap_skip= 0;
 
        /* if no text, nothing to do */
        if(!text)
                return;
        
+       text_update_drawcache(st, ar);
+
        /* make sure all the positional pointers exist */
        if(!text->curl || !text->sell || !text->lines.first || !text->lines.last)
                txt_clean_text(text);
@@ -1291,12 +1697,28 @@ void draw_text_main(SpaceText *st, ARegion *ar)
 
        /* update syntax formatting if needed */
        tmp= text->lines.first;
+       lineno= 0;
        for(i= 0; i<st->top && tmp; i++) {
                if(st->showsyntax && !tmp->format)
                        txt_format_line(st, tmp, 0);
 
-               tmp= tmp->next;
-               linecount++;
+               if(st->wordwrap) {
+                       int lines= text_get_visible_lines_no(st, lineno);
+
+                       if (wraplinecount+lines>st->top) {
+                               wrap_skip= st->top-wraplinecount;
+                               break;
+                       } else {
+                               wraplinecount+= lines;
+                               tmp= tmp->next;
+                               linecount++;
+                       }
+               } else {
+                       tmp= tmp->next;
+                       linecount++;
+               }
+
+               lineno++;
        }
 
        text_font_begin(st);
@@ -1305,7 +1727,6 @@ void draw_text_main(SpaceText *st, ARegion *ar)
 
        /* draw line numbers background */
        if(st->showlinenrs) {
-               st->linenrs_tot = (int)floor(log10((float)(linecount + st->viewlines))) + 1;
                x= TXT_OFFSET + TEXTXLOC;
 
                UI_ThemeColor(TH_GRID);
@@ -1328,7 +1749,7 @@ void draw_text_main(SpaceText *st, ARegion *ar)
                if(st->showsyntax && !tmp->format)
                        txt_format_line(st, tmp, 0);
 
-               if(st->showlinenrs) {
+               if(st->showlinenrs && !wrap_skip) {
                        /* draw line number */
                        if(tmp == text->curl)
                                UI_ThemeColor(TH_HILITE);
@@ -1344,14 +1765,16 @@ void draw_text_main(SpaceText *st, ARegion *ar)
 
                if(st->wordwrap) {
                        /* draw word wrapped text */
-                       int lines = text_draw_wrapped(st, tmp->line, x, y, winx-x, tmp->format);
+                       int lines = text_draw_wrapped(st, tmp->line, x, y, winx-x, tmp->format, wrap_skip);
                        y -= lines*st->lheight;
                }
                else {
                        /* draw unwrapped text */
-                       text_draw(st, tmp->line, st->left, 0, 1, x, y, tmp->format);
+                       text_draw(st, tmp->line, st->left, ar->winx/st->cwidth, 1, x, y, tmp->format);
                        y -= st->lheight;
                }
+
+               wrap_skip= 0;
        }
        
        /* draw other stuff */
@@ -1398,6 +1821,12 @@ void text_update_cursor_moved(bContext *C)
        text_update_character_width(st);
 
        i= txt_get_span(text->lines.first, text->sell);
+       if(st->wordwrap) {
+               int offl, offc;
+               wrap_offset(st, CTX_wm_region(C), text->sell, text->selc, &offl, &offc);
+               i+= offl;
+       }
+
        if(st->top+st->viewlines <= i || st->top > i)
                st->top= i - st->viewlines/2;
        
index a93f30ac62a982c55931bd8b3fc4c3c44a5a1d85..8196822176515ac34d94413ec43eee183cbaf21f 100644 (file)
@@ -90,12 +90,20 @@ void flatten_string_free(FlattenString *fs);
 
 int wrap_width(struct SpaceText *st, struct ARegion *ar);
 void wrap_offset(struct SpaceText *st, struct ARegion *ar, struct TextLine *linein, int cursin, int *offl, int *offc);
+void wrap_offset_in_line(struct SpaceText *st, struct ARegion *ar, struct TextLine *linep, int cursin, int *offl, int *offc);
+int text_get_char_pos(struct SpaceText *st, char *line, int cur);
+
+void text_drawcache_tag_update(struct SpaceText *st, int full);
+void text_free_caches(struct SpaceText *st);
 
 int text_file_modified(struct Text *text);
 
 int text_do_suggest_select(struct SpaceText *st, struct ARegion *ar);
 void text_pop_suggest_list();
 
+int text_get_visible_lines(struct SpaceText *st, struct ARegion *ar, char *str);
+int text_get_span_wrap(struct SpaceText *st, struct ARegion *ar, struct TextLine *from, struct TextLine *to);
+int text_get_total_lines(struct SpaceText *st, struct ARegion *ar);
 
 /* text_ops.c */
 enum { LINE_BEGIN, LINE_END, FILE_TOP, FILE_BOTTOM, PREV_CHAR, NEXT_CHAR,
index f8abe0f1900c551c5939bc6c5b59e6e7bda40e0d..8ff82ce8be05a157c4f916292b3c15aebb56b03d 100644 (file)
@@ -173,6 +173,7 @@ static int new_exec(bContext *C, wmOperator *op)
                st->top= 0;
        }
 
+       text_drawcache_tag_update(st, 1);
        WM_event_add_notifier(C, NC_TEXT|NA_ADDED, text);
 
        return OPERATOR_FINISHED;
@@ -254,6 +255,7 @@ static int open_exec(bContext *C, wmOperator *op)
                text->name = NULL;
        }
 
+       text_drawcache_tag_update(st, 1);
        WM_event_add_notifier(C, NC_TEXT|NA_ADDED, text);
 
        MEM_freeN(op->customdata);
@@ -315,6 +317,7 @@ static int reload_exec(bContext *C, wmOperator *op)
 
        text_update_edited(text);
        text_update_cursor_moved(C);
+       text_drawcache_tag_update(CTX_wm_space_text(C), 1);
        WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
 
        return OPERATOR_FINISHED;
@@ -357,6 +360,8 @@ static int unlink_exec(bContext *C, wmOperator *op)
 
        unlink_text(bmain, text);
        free_libblock(&bmain->text, text);
+
+       text_drawcache_tag_update(st, 1);
        WM_event_add_notifier(C, NC_TEXT|NA_REMOVED, NULL);
 
        return OPERATOR_FINISHED;
@@ -738,6 +743,8 @@ static int paste_exec(bContext *C, wmOperator *op)
        if(!buf)
                return OPERATOR_CANCELLED;
 
+       text_drawcache_tag_update(CTX_wm_space_text(C), 0);
+
        txt_insert_buf(text, buf);
        text_update_edited(text);
 
@@ -809,6 +816,8 @@ static int cut_exec(bContext *C, wmOperator *op)
 {
        Text *text= CTX_data_edit_text(C);
 
+       text_drawcache_tag_update(CTX_wm_space_text(C), 0);
+
        txt_copy_clipboard(text);
        txt_delete_selected(text);
 
@@ -840,6 +849,8 @@ static int indent_exec(bContext *C, wmOperator *op)
 {
        Text *text= CTX_data_edit_text(C);
 
+       text_drawcache_tag_update(CTX_wm_space_text(C), 0);
+
        if(txt_has_sel(text)) {
                txt_order_cursors(text);
                indent(text);
@@ -874,6 +885,8 @@ static int unindent_exec(bContext *C, wmOperator *op)
        Text *text= CTX_data_edit_text(C);
 
        if(txt_has_sel(text)) {
+               text_drawcache_tag_update(CTX_wm_space_text(C), 0);
+
                txt_order_cursors(text);
                unindent(text);
 
@@ -909,6 +922,8 @@ static int line_break_exec(bContext *C, wmOperator *op)
        int a, curts;
        int space = (text->flags & TXT_TABSTOSPACES) ? st->tabnumber : 1;
 
+       text_drawcache_tag_update(st, 0);
+
        // double check tabs/spaces before splitting the line
        curts= setcurr_tab_spaces(text, space);
        txt_split_curline(text);
@@ -952,6 +967,8 @@ static int comment_exec(bContext *C, wmOperator *op)
        Text *text= CTX_data_edit_text(C);
 
        if(txt_has_sel(text)) {
+               text_drawcache_tag_update(CTX_wm_space_text(C), 0);
+
                txt_order_cursors(text);
                comment(text);
                text_update_edited(text);
@@ -983,6 +1000,8 @@ static int uncomment_exec(bContext *C, wmOperator *op)
        Text *text= CTX_data_edit_text(C);
 
        if(txt_has_sel(text)) {
+               text_drawcache_tag_update(CTX_wm_space_text(C), 0);
+
                txt_order_cursors(text);
                uncomment(text);
                text_update_edited(text);
@@ -1130,6 +1149,7 @@ static int convert_whitespace_exec(bContext *C, wmOperator *op)
 
        text_update_edited(text);
        text_update_cursor_moved(C);
+       text_drawcache_tag_update(st, 1);
        WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
 
        return OPERATOR_FINISHED;
@@ -1317,146 +1337,360 @@ static EnumPropertyItem move_type_items[]= {
        {NEXT_PAGE, "NEXT_PAGE", 0, "Next Page", ""},
        {0, NULL, 0, NULL, NULL}};
 
+/* get cursor position in line by relative wrapped line and column positions */
+static int text_get_cursor_rel(SpaceText* st, ARegion *ar, TextLine *linein, int rell, int relc)
+{
+       int i, j, start, end, chars, max, chop, curs, loop, endj, found, selc;
+       char ch;
+
+       max= wrap_width(st, ar);
+
+       selc= start= chars= endj= curs= found= 0;
+       end= max;
+       chop= loop= 1;
+
+       for(i=0, j=0; loop; j++) {
+               /* Mimic replacement of tabs */
+               ch= linein->line[j];
+               if(ch=='\t') {
+                       chars= st->tabnumber-i%st->tabnumber;
+                       ch= ' ';
+               }
+               else chars= 1;
+
+               while(chars--) {
+                       if(rell==0 && i-start==relc) {
+                               /* current position could be wrapped to next line */
+                               /* this should be checked when end of current line would be reached */
+                               selc= j;
+                               found= 1;
+                       }
+                       else if(i-end==relc) {
+                               curs= j;
+                       }
+                       if(i-start>=max) {
+                               if(found) {
+                                       /* exact cursor position was found, check if it's */
+                                       /* still on needed line (hasn't been wrapped) */
+                                       if(selc>endj && !chop) selc= endj;
+                                       loop= 0;
+                                       break;
+                               }
+
+                               if(chop) endj= j;
+
+                               start= end;
+                               end += max;
+                               chop= 1;
+                               rell--;
+
+                               if(rell==0 && i-start>=relc) {
+                                       selc= curs;
+                                       loop= 0;
+                                       break;
+                               }
+                       }
+                       else if (ch=='\0') {
+                               if(!found) selc= linein->len;
+                               loop= 0;
+                               break;
+                       }
+                       else if(ch==' ' || ch=='-') {
+                               if(found) {
+                                       loop= 0;
+                                       break;
+                               }
+
+                               if(rell==0 && i-start>=relc) {
+                                       selc= curs;
+                                       loop= 0;
+                                       break;
+                               }
+                               end= i+1;
+                               endj= j;
+                               chop= 0;
+                       }
+                       i++;
+               }
+       }
+
+  return selc;
+}
+
+static int cursor_skip_find_line(SpaceText* st, ARegion *ar, Text *text,
+       int lines, TextLine **linep, int *charp, int *rell, int *relc)
+{
+       int offl, offc, visible_lines;
+
+       wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
+       *relc= text_get_char_pos(st, (*linep)->line, *charp) + offc;
+       *rell= lines;
+
+       /* handle current line */
+       if(lines>0) {
+               visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
+
+               if(*rell-visible_lines+offl>=0) {
+                       if(!(*linep)->next) {
+                               if(offl < visible_lines-1) {
+                                       *rell= visible_lines-1;
+                                       return 1;
+                               }
+
+                               *charp= (*linep)->len;
+                               return 0;
+                       }
+
+                       *rell-= visible_lines-offl;
+                       *linep=(*linep)->next;
+               } else {
+                       *rell+= offl;
+                       return 1;
+               }
+       } else {
+               if(*rell+offl<=0) {
+                       if(!(*linep)->prev) {
+                               if(offl) {
+                                       *rell= 0;
+                                       return 1;
+                               }
+
+                               *charp= 0;
+                               return 0;
+                       }
+
+                       *rell+= offl;
+                       *linep=(*linep)->prev;
+               } else {
+                       *rell+= offl;
+                       return 1;
+               }
+       }
+
+       /* skip lines and find destination line and offsets */
+       while(*linep) {
+               visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
+
+               if(lines<0) { /* moving top */
+                       if(*rell+visible_lines >= 0) {
+                               *rell+= visible_lines;
+                               break;
+                       }
+
+                       if(!(*linep)->prev) {
+                               *rell= 0;
+                               break;
+                       }
+
+                       *rell+= visible_lines;
+                       *linep=(*linep)->prev;
+               } else { /* moving bottom */
+                       if(*rell-visible_lines < 0) break;
+
+                       if(!(*linep)->next) {
+                               *rell= visible_lines-1;
+                               break;
+                       }
+
+                       *rell-= visible_lines;
+                       *linep=(*linep)->next;
+               }
+       }
+
+       return 1;
+}
+
 static void wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
 {
        Text *text= st->text;
-       int offl, offc, lin;
+       TextLine **linep;
+       int *charp;
+       int oldl, oldc, i, j, max, start, end, chars, endj, chop, loop;
+       char ch;
 
        text_update_character_width(st);
 
-       lin= txt_get_span(text->lines.first, text->sell);
-       wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
+       if (sel) linep= &text->sell, charp= &text->selc;
+       else linep= &text->curl, charp= &text->curc;
 
-       if (sel) {
-               txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, -offc);
-               text->selc= -offc;
-       } else {
-               txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, -offc);
-               text->curc= -offc;
-               txt_pop_sel(text);
+       oldc= *charp;
+       oldl= txt_get_span(text->lines.first, *linep);
+
+       max= wrap_width(st, ar);
+
+       start= chars= endj= 0;
+       end= max;
+       chop= loop= 1;
+       *charp= 0;
+
+       for(i=0, j=0; loop; j++) {
+               /* Mimic replacement of tabs */
+               ch= (*linep)->line[j];
+               if(ch=='\t') {
+                       chars= st->tabnumber-i%st->tabnumber;
+                       ch= ' ';
+               }
+               else chars= 1;
+
+               while(chars--) {
+                       if(i-start>=max) {
+                               *charp= endj;
+
+                               if(j>=oldc) {
+                                       loop= 0;
+                                       break;
+                               }
+
+                               if(chop) endj= j;
+
+                               start= end;
+                               end += max;
+                               chop= 0;
+                       }
+                       else if(ch==' ' || ch=='-' || ch=='\0') {
+                               if(j>=oldc) {
+                                       loop= 0;
+                                       break;
+                               }
+
+                               end= i+1;
+                               endj= j+1;
+                               chop= 0;
+                       }
+                       i++;
+               }
        }
+
+       if (!sel) txt_pop_sel(text);
+       txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, oldl, *charp);
 }
 
 static void wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
 {
        Text *text= st->text;
-       int offl, offc, lin, startl, c;
+       TextLine **linep;
+       int *charp;
+       int oldl, oldc, i, j, max, start, end, chars, endj, chop, loop;
+       char ch;
 
        text_update_character_width(st);
 
-       lin= txt_get_span(text->lines.first, text->sell);
-       wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
-       startl= offl;
-       c= text->selc;
-       while (offl==startl && text->sell->line[c]!='\0') {
-               c++;
-               wrap_offset(st, ar, text->sell, c, &offl, &offc);
-       } if (offl!=startl) c--;
-
-       if (sel) {
-               txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, c);
-               text->selc= c;
-       } else {
-               txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, c);
-               text->curc= c;
-               txt_pop_sel(text);
+       if (sel) linep= &text->sell, charp= &text->selc;
+       else linep= &text->curl, charp= &text->curc;
+
+       oldc= *charp;
+       oldl= txt_get_span(text->lines.first, *linep);
+
+       max= wrap_width(st, ar);
+
+       start= chars= endj= 0;
+       end= max;
+       chop= loop= 1;
+       *charp= 0;
+
+       for(i=0, j=0; loop; j++) {
+               /* Mimic replacement of tabs */
+               ch= (*linep)->line[j];
+               if(ch=='\t') {
+                       chars= st->tabnumber-i%st->tabnumber;
+                       ch= ' ';
+               }
+               else chars= 1;
+
+               while(chars--) {
+                       if(i-start>=max) {
+                               if(endj>=oldc) {
+                                       *charp= endj;
+                                       loop= 0;
+                                       break;
+                               }
+
+                               if(chop) endj= j;
+
+                               start= end;
+                               end += max;
+                               chop= 0;
+                       } else if(ch=='\0') {
+                               *charp= (*linep)->len;
+                               loop= 0;
+                               break;
+                       } else if(ch==' ' || ch=='-') {
+                               end= i+1;
+                               endj= j;
+                               chop= 0;
+                       }
+                       i++;
+               }
        }
+
+       if (!sel) txt_pop_sel(text);
+       txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, oldl, *charp);
 }
 
 static void wrap_move_up(SpaceText *st, ARegion *ar, short sel)
 {
        Text *text= st->text;
-       int offl, offl_1, offc, fromline, toline, c, target;
+       TextLine **linep;
+       int *charp;
+       int oldl, oldc, offl, offc, col, newl;
 
        text_update_character_width(st);
 
-       wrap_offset(st, ar, text->sell, 0, &offl_1, &offc);
-       wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
-       fromline= toline= txt_get_span(text->lines.first, text->sell);
-       target= text->selc + offc;
+       if (sel) linep= &text->sell, charp= &text->selc;
+       else linep= &text->curl, charp= &text->curc;
 
-       if (offl==offl_1) {
-               if (!text->sell->prev) {
-                       txt_move_bol(text, sel);
-                       return;
-               }
-               toline--;
-               c= text->sell->prev->len; /* End of prev. line */
-               wrap_offset(st, ar, text->sell->prev, c, &offl, &offc);
-               c= -offc+target;
+       /* store previous position */
+       oldc= *charp;
+       newl= oldl= txt_get_span(text->lines.first, *linep);
+
+       wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
+       col= text_get_char_pos(st, (*linep)->line, *charp) + offc;
+       if(offl) {
+               *charp= text_get_cursor_rel(st, ar, *linep, offl-1, col);
        } else {
-               c= -offc-1; /* End of prev. line */
-               wrap_offset(st, ar, text->sell, c, &offl, &offc);
-               c= -offc+target;
-       }
-       if (c<0) c=0;
-
-       if (sel) {
-               txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
-               if (toline<fromline) text->sell= text->sell->prev;
-               if(text->sell) {
-                       if (c>text->sell->len) c= text->sell->len;
-                       text->selc= c;
-               }
-       } 
-       else if(text->curl) {
-               txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
-               if (toline<fromline) text->curl= text->curl->prev;
-               if(text->curl) {
-                       if (c>text->curl->len) c= text->curl->len;
-                       text->curc= c;
-                       txt_pop_sel(text);
-               }
+               if((*linep)->prev) {
+                       int visible_lines;
+
+                       *linep= (*linep)->prev;
+                       visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
+                       *charp= text_get_cursor_rel(st, ar, *linep, visible_lines-1, col);
+               } else *charp= 0;
        }
+
+       if (!sel) txt_pop_sel(text);
+       txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, newl, *charp);
 }
 
 static void wrap_move_down(SpaceText *st, ARegion *ar, short sel)
 {
        Text *text= st->text;
-       int offl, startoff, offc, fromline, toline, c, target;
+       TextLine **linep;
+       int *charp;
+       int oldl, oldc, offl, offc, col, newl, visible_lines;
 
        text_update_character_width(st);
 
-       wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
-       fromline= toline= txt_get_span(text->lines.first, text->sell);
-       target= text->selc + offc;
-       startoff= offl;
-       c= text->selc;
-       while (offl==startoff && text->sell->line[c]!='\0') {
-               c++;
-               wrap_offset(st, ar, text->sell, c, &offl, &offc);
-       }
+       if (sel) linep= &text->sell, charp= &text->selc;
+       else linep= &text->curl, charp= &text->curc;
 
-       if (text->sell->line[c]=='\0') {
-               if (!text->sell->next) {
-                       txt_move_eol(text, sel);
-                       return;
-               }
-               toline++;
-               c= target;
+       /* store previous position */
+       oldc= *charp;
+       newl= oldl= txt_get_span(text->lines.first, *linep);
+
+       wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
+       col= text_get_char_pos(st, (*linep)->line, *charp) + offc;
+       visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
+       if(offl<visible_lines-1) {
+               *charp= text_get_cursor_rel(st, ar, *linep, offl+1, col);
        } else {
-               c += target;
-               if (c > text->sell->len) c= text->sell->len;
-       }
-       if (c<0) c=0;
-
-       if (sel) {
-               txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
-               if (toline>fromline) text->sell= text->sell->next;
-               if(text->sell) {
-                       if (c>text->sell->len) c= text->sell->len;
-                       text->selc= c;
-               }
-       } 
-       else if(text->curl) {
-               txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
-               if (toline>fromline) text->curl= text->curl->next;
-               if(text->curl) {
-                       if (c > text->curl->len) c= text->curl->len;
-                       text->curc= c;
-                       txt_pop_sel(text);
-               }
+               if((*linep)->next) {
+                       *linep= (*linep)->next;
+                       *charp= text_get_cursor_rel(st, ar, *linep, 0, col);
+               } else *charp= (*linep)->len;
        }
+
+       if (!sel) txt_pop_sel(text);
+       txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, newl, *charp);
 }
 
 /* Moves the cursor vertically by the specified number of lines.
@@ -1465,7 +1699,7 @@ static void wrap_move_down(SpaceText *st, ARegion *ar, short sel)
 
  This is to replace screen_skip for PageUp/Down operations.
  */
-static void cursor_skip(Text *text, int lines, int sel)
+static void cursor_skip(SpaceText* st, ARegion *ar, Text *text, int lines, int sel)
 {
        TextLine **linep;
        int oldl, oldc, *charp;
@@ -1475,13 +1709,21 @@ static void cursor_skip(Text *text, int lines, int sel)
        oldl= txt_get_span(text->lines.first, *linep);
        oldc= *charp;
 
-       while (lines>0 && (*linep)->next) {
-               *linep= (*linep)->next;
-               lines--;
-       }
-       while (lines<0 && (*linep)->prev) {
-               *linep= (*linep)->prev;
-               lines++;
+       if(st && ar && st->wordwrap) {
+               int rell, relc;
+
+               /* find line and offsets inside it needed to set cursor position */
+               if(cursor_skip_find_line(st, ar, text, lines, linep, charp, &rell, &relc))
+                 *charp= text_get_cursor_rel (st, ar, *linep, rell, relc);
+       } else {
+               while (lines>0 && (*linep)->next) {
+                       *linep= (*linep)->next;
+                       lines--;
+               }
+               while (lines<0 && (*linep)->prev) {
+                       *linep= (*linep)->prev;
+                       lines++;
+               }
        }
 
        if (*charp > (*linep)->len) *charp= (*linep)->len;
@@ -1546,18 +1788,18 @@ static int move_cursor(bContext *C, int type, int select)
                        break;
 
                case PREV_PAGE:
-                       if(st) cursor_skip(text, -st->viewlines, select);
-                       else cursor_skip(text, -10, select);
+                       if(st) cursor_skip(st, ar, st->text, -st->viewlines, select);
+                       else cursor_skip(NULL, NULL, text, -10, select);
                        break;
 
                case NEXT_PAGE:
-                       if(st) cursor_skip(text, st->viewlines, select);
-                       else cursor_skip(text, 10, select);
+                       if(st) cursor_skip(st, ar, st->text, st->viewlines, select);
+                       else cursor_skip(NULL, NULL, text, 10, select);
                        break;
        }
 
        text_update_cursor_moved(C);
-       WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
+       WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
 
        return OPERATOR_FINISHED;
 }
@@ -1665,6 +1907,8 @@ static int delete_exec(bContext *C, wmOperator *op)
        Text *text= CTX_data_edit_text(C);
        int type= RNA_enum_get(op->ptr, "type");
 
+       text_drawcache_tag_update(CTX_wm_space_text(C), 0);
+
        if(type == DEL_PREV_WORD)
                txt_backspace_word(text);
        else if(type == DEL_PREV_CHAR)
@@ -1729,13 +1973,13 @@ void TEXT_OT_overwrite_toggle(wmOperatorType *ot)
 /******************* scroll operator **********************/
 
 /* Moves the view vertically by the specified number of lines */
-static void screen_skip(SpaceText *st, int lines)
+static void screen_skip(SpaceText *st, ARegion *ar, int lines)
 {
        int last;
 
-        st->top += lines;
+       st->top += lines;
 
-       last= txt_get_span(st->text->lines.first, st->text->lines.last);
+       last= text_get_total_lines(st, ar);
        last= last - (st->viewlines/2);
        
        if(st->top>last) st->top= last;
@@ -1756,12 +2000,14 @@ typedef struct TextScroll {
 static int scroll_exec(bContext *C, wmOperator *op)
 {
        SpaceText *st= CTX_wm_space_text(C);
+       ARegion *ar= CTX_wm_region(C);
+
        int lines= RNA_int_get(op->ptr, "lines");
 
        if(lines == 0)
                return OPERATOR_CANCELLED;
 
-       screen_skip(st, lines*U.wheellinescroll);
+       screen_skip(st, ar, lines*U.wheellinescroll);
 
        ED_area_tag_redraw(CTX_wm_area(C));
 
@@ -1771,6 +2017,7 @@ static int scroll_exec(bContext *C, wmOperator *op)
 static void scroll_apply(bContext *C, wmOperator *op, wmEvent *event)
 {
        SpaceText *st= CTX_wm_space_text(C);
+       ARegion *ar= CTX_wm_region(C);
        TextScroll *tsc= op->customdata;
        short *mval= event->mval;
 
@@ -1792,7 +2039,7 @@ static void scroll_apply(bContext *C, wmOperator *op, wmEvent *event)
                tsc->delta[1]= (tsc->hold[1]-mval[1])*st->pix_per_line;
        
        if(tsc->delta[0] || tsc->delta[1]) {
-               screen_skip(st, tsc->delta[1]);
+               screen_skip(st, ar, tsc->delta[1]);
 
                tsc->lines += tsc->delta[1];
 
@@ -1969,7 +2216,7 @@ static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel)
        Text *text= st->text;
        TextLine **linep;
        int *charp;
-       int w;
+       int w, tabs;
 
        text_update_character_width(st);
 
@@ -1987,16 +2234,28 @@ static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel)
        x = (x/st->cwidth) + st->left;
        
        if(st->wordwrap) {
-               int i, j, endj, curs, max, chop, start, end, chars, loop;
+               int i, j, endj, curs, max, chop, start, end, chars, loop, found;
                char ch;
 
                /* Point to first visible line */
                *linep= text->lines.first;
-               for(i=0; i<st->top && (*linep)->next; i++) *linep= (*linep)->next;
+               i= st->top;
+               while(i>0 && *linep) {
+                       int lines= text_get_visible_lines(st, ar, (*linep)->line);
+
+                       if (i-lines<0) {
+                               y+= i;
+                               break;
+                       } else {
+                               *linep= (*linep)->next;
+                               i-= lines;
+                       }
+               }
 
                max= wrap_width(st, ar);
 
                loop= 1;
+               found= 0;
                while(loop && *linep) {
                        start= 0;
                        end= max;
@@ -2004,12 +2263,14 @@ static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel)
                        chars= 0;
                        curs= 0;
                        endj= 0;
+                       tabs= 0;
                        for(i=0, j=0; loop; j++) {
 
                                /* Mimic replacement of tabs */
                                ch= (*linep)->line[j];
                                if(ch=='\t') {
                                        chars= st->tabnumber-i%st->tabnumber;
+                                       tabs+= chars-1;
                                        ch= ' ';
                                }
                                else
@@ -2021,22 +2282,34 @@ static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel)
                                                *charp= endj;
                                                loop= 0;
                                                break;
-                                       /* Exactly at the cursor, done */
+                                       /* Exactly at the cursor */
                                        }
                                        else if(y==0 && i-start==x) {
+                                               /* current position could be wrapped to next line */
+                                               /* this should be checked when end of current line would be reached */
                                                *charp= curs= j;
-                                               loop= 0;
-                                               break;
+                                               found= 1;
                                        /* Prepare curs for next wrap */
                                        }
                                        else if(i-end==x) {
                                                curs= j;
                                        }
                                        if(i-start>=max) {
+                                               if(found) {
+                                                       /* exact cursor position was found, check if it's */
+                                                       /* still on needed line (hasn't been wrapped) */
+                                                       if(*charp>endj && !chop) (*charp)= endj;
+                                                       loop= 0;
+                                                       break;
+                                               }
+
                                                if(chop) endj= j;
-                                               y--;
                                                start= end;
                                                end += max;
+
+                                               if(start-tabs<(*linep)->len)
+                                                       y--;
+
                                                chop= 1;
                                                if(y==0 && i-start>=x) {
                                                        *charp= curs;
@@ -2045,6 +2318,11 @@ static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel)
                                                }
                                        }
                                        else if(ch==' ' || ch=='-' || ch=='\0') {
+                                               if(found) {
+                                                       loop= 0;
+                                                       break;
+                                               }
+
                                                if(y==0 && i-start>=x) {
                                                        *charp= curs;
                                                        loop= 0;
@@ -2102,7 +2380,7 @@ static void set_cursor_apply(bContext *C, wmOperator *op, wmEvent *event)
 
        if(event->mval[1]<0 || event->mval[1]>ar->winy) {
                int d= (scu->old[1]-event->mval[1])*st->pix_per_line;
-               if(d) screen_skip(st, d);
+               if(d) screen_skip(st, ar, d);
 
                set_cursor_to_pos(st, ar, event->mval[0], event->mval[1]<0?0:ar->winy, 1);
 
@@ -2290,6 +2568,8 @@ static int insert_exec(bContext *C, wmOperator *op)
        char *str;
        int done = 0, i;
 
+       text_drawcache_tag_update(st, 0);
+
        str= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
 
        if(st && st->overwrite) {
@@ -2396,6 +2676,7 @@ static int find_and_replace(bContext *C, wmOperator *op, short mode)
                                        }
                                        text_update_cursor_moved(C);
                                        WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
+                                       text_drawcache_tag_update(CTX_wm_space_text(C), 1);
                                }
                                else if(mode==TEXT_MARK_ALL) {
                                        char color[4];
@@ -2741,6 +3022,7 @@ void ED_text_undo_step(bContext *C, int step)
        text_update_edited(text);
 
        text_update_cursor_moved(C);
+       text_drawcache_tag_update(CTX_wm_space_text(C), 1);
        WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
 }
 
index ce038ee4a95495c1724e4c31fb0815df373b9bc6..0e2eea0b942f1564e64c564c4bf7cd3dcb9caa3e 100644 (file)
@@ -318,6 +318,8 @@ typedef struct SpaceText {
 
        char findstr[256];              /* ST_MAX_FIND_STR */
        char replacestr[256];   /* ST_MAX_FIND_STR */
+
+       void *drawcache; /* cache for faster drawing */
 } SpaceText;
 
 typedef struct Script {