From 54ffc23cd59164180ea874b96ff9cd97753d6218 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Wed, 13 Oct 2010 06:06:39 +0000 Subject: Text space ========== 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 | 3 + source/blender/editors/space_text/space_text.c | 12 +- source/blender/editors/space_text/text_draw.c | 629 ++++++++++++++++++++---- source/blender/editors/space_text/text_intern.h | 8 + source/blender/editors/space_text/text_ops.c | 536 +++++++++++++++----- source/blender/makesdna/DNA_space_types.h | 2 + 6 files changed, 960 insertions(+), 230 deletions(-) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 0eb25a6b894..22641d51ed9 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -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; diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 7f7a07f8cf7..5ee7ca3c3ef 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -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: diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index be2d993dab5..c6036eb354b 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -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=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= 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; ashowsyntax && 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 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->linenostart, &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; iwinx, 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; iwinx, 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; itop && 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; diff --git a/source/blender/editors/space_text/text_intern.h b/source/blender/editors/space_text/text_intern.h index a93f30ac62a..81968221765 100644 --- a/source/blender/editors/space_text/text_intern.h +++ b/source/blender/editors/space_text/text_intern.h @@ -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, diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index f8abe0f1900..8ff82ce8be0 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -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 (tolinesell= 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 (tolinecurl= 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 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; itop && (*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); } diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index ce038ee4a95..0e2eea0b942 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -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 { -- cgit v1.2.3