|
Blender
V2.59
|
00001 /* 00002 * $Id: text_draw.c 36547 2011-05-08 10:29:40Z campbellbarton $ 00003 * 00004 * ***** BEGIN GPL LICENSE BLOCK ***** 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License 00016 * along with this program; if not, write to the Free Software Foundation, 00017 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 * 00019 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. 00020 * All rights reserved. 00021 * 00022 * The Original Code is: all of this file. 00023 * 00024 * Contributor(s): none yet. 00025 * 00026 * ***** END GPL LICENSE BLOCK ***** 00027 */ 00028 00034 #include <math.h> 00035 #include <stdlib.h> 00036 #include <string.h> 00037 #include <sys/stat.h> 00038 00039 #include "MEM_guardedalloc.h" 00040 00041 #include "BLF_api.h" 00042 00043 #include "BLI_blenlib.h" 00044 #include "BLI_utildefines.h" 00045 00046 #include "DNA_text_types.h" 00047 #include "DNA_space_types.h" 00048 #include "DNA_screen_types.h" 00049 #include "DNA_userdef_types.h" 00050 00051 #include "BKE_context.h" 00052 #include "BKE_suggestions.h" 00053 #include "BKE_text.h" 00054 00055 00056 #include "BIF_gl.h" 00057 00058 #include "ED_datafiles.h" 00059 #include "UI_interface.h" 00060 #include "UI_resources.h" 00061 00062 #include "text_intern.h" 00063 00064 /******************** text font drawing ******************/ 00065 // XXX, fixme 00066 #define mono blf_mono_font 00067 00068 static void text_font_begin(SpaceText *st) 00069 { 00070 BLF_size(mono, st->lheight, 72); 00071 } 00072 00073 static void text_font_end(SpaceText *UNUSED(st)) 00074 { 00075 } 00076 00077 static int text_font_draw(SpaceText *UNUSED(st), int x, int y, char *str) 00078 { 00079 BLF_position(mono, x, y, 0); 00080 BLF_draw(mono, str, 65535); /* XXX, use real length */ 00081 00082 return BLF_width(mono, str); 00083 } 00084 00085 static int text_font_draw_character(SpaceText *st, int x, int y, char c) 00086 { 00087 char str[2]; 00088 str[0]= c; 00089 str[1]= '\0'; 00090 00091 BLF_position(mono, x, y, 0); 00092 BLF_draw(mono, str, 1); 00093 00094 return st->cwidth; 00095 } 00096 00097 int text_font_width(SpaceText *UNUSED(st), const char *str) 00098 { 00099 return BLF_width(mono, str); 00100 } 00101 00102 /****************** flatten string **********************/ 00103 00104 static void flatten_string_append(FlattenString *fs, char c, int accum) 00105 { 00106 if(fs->pos>=fs->len && fs->pos>=sizeof(fs->fixedbuf)-1) { 00107 char *nbuf; int *naccum; 00108 if(fs->len) fs->len*= 2; 00109 else fs->len= sizeof(fs->fixedbuf) * 2; 00110 00111 nbuf= MEM_callocN(sizeof(*fs->buf)*fs->len, "fs->buf"); 00112 naccum= MEM_callocN(sizeof(*fs->accum)*fs->len, "fs->accum"); 00113 00114 memcpy(nbuf, fs->buf, fs->pos * sizeof(*fs->buf)); 00115 memcpy(naccum, fs->accum, fs->pos * sizeof(*fs->accum)); 00116 00117 if(fs->buf != fs->fixedbuf) { 00118 MEM_freeN(fs->buf); 00119 MEM_freeN(fs->accum); 00120 } 00121 00122 fs->buf= nbuf; 00123 fs->accum= naccum; 00124 } 00125 00126 fs->buf[fs->pos]= c; 00127 fs->accum[fs->pos]= accum; 00128 00129 fs->pos++; 00130 } 00131 00132 int flatten_string(SpaceText *st, FlattenString *fs, const char *in) 00133 { 00134 int r = 0, i = 0; 00135 00136 memset(fs, 0, sizeof(FlattenString)); 00137 fs->buf= fs->fixedbuf; 00138 fs->accum= fs->fixedaccum; 00139 00140 for(r=0, i=0; *in; r++, in++) { 00141 if(*in=='\t') { 00142 if(fs->pos && *(in-1)=='\t') 00143 i= st->tabnumber; 00144 else if(st->tabnumber > 0) 00145 i= st->tabnumber - (fs->pos%st->tabnumber); 00146 00147 while(i--) 00148 flatten_string_append(fs, ' ', r); 00149 } 00150 else 00151 flatten_string_append(fs, *in, r); 00152 } 00153 00154 return fs->pos; 00155 } 00156 00157 void flatten_string_free(FlattenString *fs) 00158 { 00159 if(fs->buf != fs->fixedbuf) 00160 MEM_freeN(fs->buf); 00161 if(fs->accum != fs->fixedaccum) 00162 MEM_freeN(fs->accum); 00163 } 00164 00165 /* Checks the specified source string for a Python built-in function name. This 00166 name must start at the beginning of the source string and must be followed by 00167 a non-identifier (see text_check_identifier(char)) or null character. 00168 00169 If a built-in function is found, the length of the matching name is returned. 00170 Otherwise, -1 is returned. */ 00171 00172 static int find_builtinfunc(char *string) 00173 { 00174 int a, i; 00175 char builtinfuncs[][9] = {"and", "as", "assert", "break", "class", "continue", "def", 00176 "del", "elif", "else", "except", "exec", "finally", 00177 "for", "from", "global", "if", "import", "in", 00178 "is", "lambda", "not", "or", "pass", "print", 00179 "raise", "return", "try", "while", "yield", "with"}; 00180 00181 for(a=0; a < sizeof(builtinfuncs)/sizeof(builtinfuncs[0]); a++) { 00182 i = 0; 00183 while(1) { 00184 /* If we hit the end of a keyword... (eg. "def") */ 00185 if(builtinfuncs[a][i]=='\0') { 00186 /* If we still have identifier chars in the source (eg. "definate") */ 00187 if(text_check_identifier(string[i])) 00188 i = -1; /* No match */ 00189 break; /* Next keyword if no match, otherwise we're done */ 00190 00191 /* If chars mismatch, move on to next keyword */ 00192 } 00193 else if(string[i]!=builtinfuncs[a][i]) { 00194 i = -1; 00195 break; /* Break inner loop, start next keyword */ 00196 } 00197 i++; 00198 } 00199 if(i>0) break; /* If we have a match, we're done */ 00200 } 00201 return i; 00202 } 00203 00204 /* Checks the specified source string for a Python special name. This name must 00205 start at the beginning of the source string and must be followed by a non- 00206 identifier (see text_check_identifier(char)) or null character. 00207 00208 If a special name is found, the length of the matching name is returned. 00209 Otherwise, -1 is returned. */ 00210 00211 static int find_specialvar(char *string) 00212 { 00213 int i = 0; 00214 /* Check for "def" */ 00215 if(string[0]=='d' && string[1]=='e' && string[2]=='f') 00216 i = 3; 00217 /* Check for "class" */ 00218 else if(string[0]=='c' && string[1]=='l' && string[2]=='a' && string[3]=='s' && string[4]=='s') 00219 i = 5; 00220 /* If next source char is an identifier (eg. 'i' in "definate") no match */ 00221 if(i==0 || text_check_identifier(string[i])) 00222 return -1; 00223 return i; 00224 } 00225 00226 static int find_decorator(char *string) 00227 { 00228 if(string[0] == '@') { 00229 int i = 1; 00230 while(text_check_identifier(string[i])) { 00231 i++; 00232 } 00233 return i; 00234 } 00235 return -1; 00236 } 00237 00238 static int find_bool(char *string) 00239 { 00240 int i = 0; 00241 /* Check for "False" */ 00242 if(string[0]=='F' && string[1]=='a' && string[2]=='l' && string[3]=='s' && string[4]=='e') 00243 i = 5; 00244 /* Check for "True" */ 00245 else if(string[0]=='T' && string[1]=='r' && string[2]=='u' && string[3]=='e') 00246 i = 4; 00247 /* Check for "None" */ 00248 else if(string[0]=='N' && string[1]=='o' && string[2]=='n' && string[3]=='e') 00249 i = 4; 00250 /* If next source char is an identifier (eg. 'i' in "definate") no match */ 00251 if(i==0 || text_check_identifier(string[i])) 00252 return -1; 00253 return i; 00254 } 00255 00256 /* Ensures the format string for the given line is long enough, reallocating 00257 as needed. Allocation is done here, alone, to ensure consistency. */ 00258 static int text_check_format_len(TextLine *line, unsigned int len) 00259 { 00260 if(line->format) { 00261 if(strlen(line->format) < len) { 00262 MEM_freeN(line->format); 00263 line->format = MEM_mallocN(len+2, "SyntaxFormat"); 00264 if(!line->format) return 0; 00265 } 00266 } 00267 else { 00268 line->format = MEM_mallocN(len+2, "SyntaxFormat"); 00269 if(!line->format) return 0; 00270 } 00271 00272 return 1; 00273 } 00274 00275 /* Formats the specified line. If do_next is set, the process will move on to 00276 the succeeding line if it is affected (eg. multiline strings). Format strings 00277 may contain any of the following characters: 00278 '_' Whitespace 00279 '#' Comment text 00280 '!' Punctuation and other symbols 00281 'n' Numerals 00282 'l' String letters 00283 'v' Special variables (class, def) 00284 'b' Built-in names (print, for, etc.) 00285 'q' Other text (identifiers, etc.) 00286 It is terminated with a null-terminator '\0' followed by a continuation 00287 flag indicating whether the line is part of a multi-line string. */ 00288 00289 static void txt_format_line(SpaceText *st, TextLine *line, int do_next) 00290 { 00291 FlattenString fs; 00292 char *str, *fmt, orig, cont, find, prev = ' '; 00293 int len, i; 00294 00295 /* Get continuation from previous line */ 00296 if(line->prev && line->prev->format != NULL) { 00297 fmt= line->prev->format; 00298 cont = fmt[strlen(fmt)+1]; /* Just after the null-terminator */ 00299 } 00300 else cont = 0; 00301 00302 /* Get original continuation from this line */ 00303 if(line->format != NULL) { 00304 fmt= line->format; 00305 orig = fmt[strlen(fmt)+1]; /* Just after the null-terminator */ 00306 } 00307 else orig = 0xFF; 00308 00309 flatten_string(st, &fs, line->line); 00310 str = fs.buf; 00311 len = strlen(str); 00312 if(!text_check_format_len(line, len)) { 00313 flatten_string_free(&fs); 00314 return; 00315 } 00316 fmt = line->format; 00317 00318 while(*str) { 00319 /* Handle escape sequences by skipping both \ and next char */ 00320 if(*str == '\\') { 00321 *fmt = prev; fmt++; str++; 00322 if(*str == '\0') break; 00323 *fmt = prev; fmt++; str++; 00324 continue; 00325 } 00326 /* Handle continuations */ 00327 else if(cont) { 00328 /* Triple strings ("""...""" or '''...''') */ 00329 if(cont & TXT_TRISTR) { 00330 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\''; 00331 if(*str==find && *(str+1)==find && *(str+2)==find) { 00332 *fmt = 'l'; fmt++; str++; 00333 *fmt = 'l'; fmt++; str++; 00334 cont = 0; 00335 } 00336 /* Handle other strings */ 00337 } 00338 else { 00339 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\''; 00340 if(*str == find) cont = 0; 00341 } 00342 00343 *fmt = 'l'; 00344 } 00345 /* Not in a string... */ 00346 else { 00347 /* Deal with comments first */ 00348 if(prev == '#' || *str == '#') 00349 *fmt = '#'; 00350 /* Strings */ 00351 else if(*str == '"' || *str == '\'') { 00352 find = *str; 00353 cont = (*str== '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR; 00354 if(*(str+1) == find && *(str+2) == find) { 00355 *fmt = 'l'; fmt++; str++; 00356 *fmt = 'l'; fmt++; str++; 00357 cont |= TXT_TRISTR; 00358 } 00359 *fmt = 'l'; 00360 } 00361 /* Whitespace (all ws. has been converted to spaces) */ 00362 else if(*str == ' ') 00363 *fmt = '_'; 00364 /* Numbers (digits not part of an identifier and periods followed by digits) */ 00365 else if((prev != 'q' && text_check_digit(*str)) || (*str == '.' && text_check_digit(*(str+1)))) 00366 *fmt = 'n'; 00367 /* Booleans */ 00368 else if(prev != 'q' && (i=find_bool(str)) != -1) 00369 if(i>0) { 00370 while(i>1) { 00371 *fmt = 'n'; fmt++; str++; 00372 i--; 00373 } 00374 *fmt = 'n'; 00375 } 00376 else 00377 *fmt = 'q'; 00378 /* Punctuation */ 00379 else if(text_check_delim(*str)) 00380 *fmt = '!'; 00381 /* Identifiers and other text (no previous ws. or delims. so text continues) */ 00382 else if(prev == 'q') 00383 *fmt = 'q'; 00384 /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */ 00385 else { 00386 /* Special vars(v) or built-in keywords(b) */ 00387 if((i=find_specialvar(str)) != -1) 00388 prev = 'v'; 00389 else if((i=find_builtinfunc(str)) != -1) 00390 prev = 'b'; 00391 else if((i=find_decorator(str)) != -1) 00392 prev = 'v'; /* could have a new color for this */ 00393 if(i>0) { 00394 while(i>1) { 00395 *fmt = prev; fmt++; str++; 00396 i--; 00397 } 00398 *fmt = prev; 00399 } 00400 else 00401 *fmt = 'q'; 00402 } 00403 } 00404 prev = *fmt; 00405 fmt++; 00406 str++; 00407 } 00408 00409 /* Terminate and add continuation char */ 00410 *fmt = '\0'; fmt++; 00411 *fmt = cont; 00412 00413 /* Debugging */ 00414 //print_format(st, line); 00415 00416 /* If continuation has changed and we're allowed, process the next line */ 00417 if(cont!=orig && do_next && line->next) { 00418 txt_format_line(st, line->next, do_next); 00419 } 00420 00421 flatten_string_free(&fs); 00422 } 00423 00424 #if 0 00425 /* Formats every line of the current text */ 00426 static void txt_format_text(SpaceText *st) 00427 { 00428 TextLine *linep; 00429 00430 if(!st->text) return; 00431 00432 for(linep=st->text->lines.first; linep; linep=linep->next) 00433 txt_format_line(st, linep, 0); 00434 } 00435 #endif 00436 00437 /* Sets the current drawing color based on the format character specified */ 00438 static void format_draw_color(char formatchar) 00439 { 00440 switch (formatchar) { 00441 case '_': /* Whitespace */ 00442 break; 00443 case '!': /* Symbols */ 00444 UI_ThemeColorBlend(TH_TEXT, TH_BACK, 0.5f); 00445 break; 00446 case '#': /* Comments */ 00447 UI_ThemeColor(TH_SYNTAX_C); 00448 break; 00449 case 'n': /* Numerals */ 00450 UI_ThemeColor(TH_SYNTAX_N); 00451 break; 00452 case 'l': /* Strings */ 00453 UI_ThemeColor(TH_SYNTAX_L); 00454 break; 00455 case 'v': /* Specials: class, def */ 00456 UI_ThemeColor(TH_SYNTAX_V); 00457 break; 00458 case 'b': /* Keywords: for, print, etc. */ 00459 UI_ThemeColor(TH_SYNTAX_B); 00460 break; 00461 case 'q': /* Other text (identifiers) */ 00462 default: 00463 UI_ThemeColor(TH_TEXT); 00464 break; 00465 } 00466 } 00467 00468 /************************** draw text *****************************/ 00469 00470 /***********************/ /* 00471 00472 Notes on word-wrap 00473 -- 00474 All word-wrap functions follow the algorithm below to maintain consistency. 00475 line The line to wrap (tabs converted to spaces) 00476 view_width The maximum number of characters displayable in the region 00477 This equals region_width/font_width for the region 00478 wrap_chars Characters that allow wrapping. This equals [' ', '\t', '-'] 00479 00480 def wrap(line, view_width, wrap_chars): 00481 draw_start = 0 00482 draw_end = view_width 00483 pos = 0 00484 for c in line: 00485 if pos-draw_start >= view_width: 00486 print line[draw_start:draw_end] 00487 draw_start = draw_end 00488 draw_end += view_width 00489 elif c in wrap_chars: 00490 draw_end = pos+1 00491 pos += 1 00492 print line[draw_start:] 00493 00494 */ /***********************/ 00495 00496 int wrap_width(SpaceText *st, ARegion *ar) 00497 { 00498 int winx= ar->winx - TXT_SCROLL_WIDTH; 00499 int x, max; 00500 00501 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET; 00502 max= st->cwidth ? (winx-x)/st->cwidth : 0; 00503 return max>8 ? max : 8; 00504 } 00505 00506 /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */ 00507 void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc) 00508 { 00509 Text *text; 00510 TextLine *linep; 00511 int i, j, start, end, chars, max, chop; 00512 char ch; 00513 00514 *offl= *offc= 0; 00515 00516 if(!st->text) return; 00517 if(!st->wordwrap) return; 00518 00519 text= st->text; 00520 00521 /* Move pointer to first visible line (top) */ 00522 linep= text->lines.first; 00523 i= st->top; 00524 while(i>0 && linep) { 00525 int lines= text_get_visible_lines(st, ar, linep->line); 00526 00527 /* Line before top */ 00528 if(linep == linein) { 00529 if(lines <= i) 00530 /* no visible part of line */ 00531 return; 00532 } 00533 00534 if (i-lines<0) { 00535 break; 00536 } else { 00537 linep= linep->next; 00538 (*offl)+= lines-1; 00539 i-= lines; 00540 } 00541 } 00542 00543 max= wrap_width(st, ar); 00544 00545 while(linep) { 00546 start= 0; 00547 end= max; 00548 chop= 1; 00549 chars= 0; 00550 *offc= 0; 00551 for(i=0, j=0; linep->line[j]!='\0'; j++) { 00552 00553 /* Mimic replacement of tabs */ 00554 ch= linep->line[j]; 00555 if(ch=='\t') { 00556 chars= st->tabnumber-i%st->tabnumber; 00557 if(linep==linein && i<cursin) cursin += chars-1; 00558 ch= ' '; 00559 } 00560 else 00561 chars= 1; 00562 00563 while(chars--) { 00564 if(i-start>=max) { 00565 if(chop && linep==linein && i >= cursin) { 00566 if (i==cursin) { 00567 (*offl)++; 00568 *offc -= end-start; 00569 } 00570 00571 return; 00572 } 00573 00574 (*offl)++; 00575 *offc -= end-start; 00576 00577 start= end; 00578 end += max; 00579 chop= 1; 00580 } 00581 else if(ch==' ' || ch=='-') { 00582 end = i+1; 00583 chop= 0; 00584 if(linep==linein && i >= cursin) 00585 return; 00586 } 00587 i++; 00588 } 00589 } 00590 if(linep==linein) break; 00591 linep= linep->next; 00592 } 00593 } 00594 00595 void wrap_offset_in_line(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc) 00596 { 00597 int i, j, start, end, chars, max, chop; 00598 char ch; 00599 00600 *offl= *offc= 0; 00601 00602 if(!st->text) return; 00603 if(!st->wordwrap) return; 00604 00605 max= wrap_width(st, ar); 00606 00607 start= 0; 00608 end= max; 00609 chop= 1; 00610 *offc= 0; 00611 00612 for(i=0, j=0; linein->line[j]!='\0'; j++) { 00613 00614 /* Mimic replacement of tabs */ 00615 ch= linein->line[j]; 00616 if(ch=='\t') { 00617 chars= st->tabnumber-i%st->tabnumber; 00618 if(i<cursin) cursin += chars-1; 00619 ch= ' '; 00620 } 00621 else 00622 chars= 1; 00623 00624 while(chars--) { 00625 if(i-start>=max) { 00626 if(chop && i >= cursin) { 00627 if (i==cursin) { 00628 (*offl)++; 00629 *offc -= end-start; 00630 } 00631 00632 return; 00633 } 00634 00635 (*offl)++; 00636 *offc -= end-start; 00637 00638 start= end; 00639 end += max; 00640 chop= 1; 00641 } 00642 else if(ch==' ' || ch=='-') { 00643 end = i+1; 00644 chop= 0; 00645 if(i >= cursin) 00646 return; 00647 } 00648 i++; 00649 } 00650 } 00651 } 00652 00653 int text_get_char_pos(SpaceText *st, const char *line, int cur) 00654 { 00655 int a=0, i; 00656 00657 for(i=0; i<cur && line[i]; i++) { 00658 if(line[i]=='\t') 00659 a += st->tabnumber-a%st->tabnumber; 00660 else 00661 a++; 00662 } 00663 return a; 00664 } 00665 00666 static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format, int skip) 00667 { 00668 FlattenString fs; 00669 int basex, i, a, len, start, end, max, lines; 00670 00671 len= flatten_string(st, &fs, str); 00672 str= fs.buf; 00673 max= w/st->cwidth; 00674 if(max<8) max= 8; 00675 basex= x; 00676 00677 lines= 1; 00678 start= 0; 00679 end= max; 00680 for(i=0; i<len; i++) { 00681 if(i-start >= max) { 00682 /* skip hidden part of line */ 00683 if(skip) { 00684 skip--; 00685 start= end; 00686 end += max; 00687 continue; 00688 } 00689 00690 /* Draw the visible portion of text on the overshot line */ 00691 for(a=start; a<end; a++) { 00692 if(st->showsyntax && format) format_draw_color(format[a]); 00693 x += text_font_draw_character(st, x, y, str[a]); 00694 } 00695 y -= st->lheight; 00696 x= basex; 00697 lines++; 00698 start= end; 00699 end += max; 00700 00701 if(y<=0) break; 00702 } 00703 else if(str[i]==' ' || str[i]=='-') { 00704 end = i+1; 00705 } 00706 } 00707 00708 /* Draw the remaining text */ 00709 for(a=start; a<len && y > 0; a++) { 00710 if(st->showsyntax && format) 00711 format_draw_color(format[a]); 00712 00713 x += text_font_draw_character(st, x, y, str[a]); 00714 } 00715 00716 flatten_string_free(&fs); 00717 00718 return lines; 00719 } 00720 00721 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format) 00722 { 00723 FlattenString fs; 00724 int r=0, w= 0, amount; 00725 int *acc; 00726 char *in; 00727 00728 w= flatten_string(st, &fs, str); 00729 if(w < cshift) { 00730 flatten_string_free(&fs); 00731 return 0; /* String is shorter than shift */ 00732 } 00733 00734 in= fs.buf+cshift; 00735 acc= fs.accum+cshift; 00736 w= w-cshift; 00737 00738 if(draw) { 00739 if(st->showsyntax && format) { 00740 int a; 00741 format = format+cshift; 00742 00743 amount = strlen(in); 00744 if(maxwidth) 00745 amount= MIN2(amount, maxwidth); 00746 00747 for(a = 0; a < amount; a++) { 00748 format_draw_color(format[a]); 00749 x += text_font_draw_character(st, x, y, in[a]); 00750 } 00751 } 00752 else { 00753 amount = strlen(in); 00754 if(maxwidth) 00755 amount= MIN2(amount, maxwidth); 00756 00757 in[amount]= 0; 00758 text_font_draw(st, x, y, in); 00759 } 00760 } 00761 else { 00762 while(w-- && *acc++ < maxwidth) 00763 r+= st->cwidth; 00764 } 00765 00766 flatten_string_free(&fs); 00767 00768 if(cshift && r==0) 00769 return 0; 00770 else if(st->showlinenrs) 00771 return r+TXT_OFFSET+TEXTXLOC; 00772 else 00773 return r+TXT_OFFSET; 00774 } 00775 00776 /************************ cache utilities *****************************/ 00777 00778 typedef struct DrawCache { 00779 int *line_height; 00780 int total_lines, nlines; 00781 00782 /* this is needed to check cache relevance */ 00783 int winx, wordwrap, showlinenrs, tabnumber; 00784 short lheight; 00785 char cwidth; 00786 char text_id[MAX_ID_NAME]; 00787 00788 /* for partial lines recalculation */ 00789 short update_flag; 00790 int valid_head, valid_tail; /* amount of unchanged lines */ 00791 } DrawCache; 00792 00793 static void text_drawcache_init(SpaceText *st) 00794 { 00795 DrawCache *drawcache= MEM_callocN(sizeof (DrawCache), "text draw cache"); 00796 00797 drawcache->winx= -1; 00798 drawcache->nlines= BLI_countlist(&st->text->lines); 00799 drawcache->text_id[0]= '\0'; 00800 00801 st->drawcache= drawcache; 00802 } 00803 00804 static void text_update_drawcache(SpaceText *st, ARegion *ar) 00805 { 00806 DrawCache *drawcache; 00807 int full_update= 0, nlines= 0; 00808 Text *txt= st->text; 00809 00810 if(!st->drawcache) text_drawcache_init(st); 00811 00812 text_update_character_width(st); 00813 00814 drawcache= (DrawCache *)st->drawcache; 00815 nlines= drawcache->nlines; 00816 00817 /* check if full cache update is needed */ 00818 full_update|= drawcache->winx != ar->winx; /* area was resized */ 00819 full_update|= drawcache->wordwrap != st->wordwrap; /* word-wrapping option was toggled */ 00820 full_update|= drawcache->showlinenrs != st->showlinenrs; /* word-wrapping option was toggled */ 00821 full_update|= drawcache->tabnumber != st->tabnumber; /* word-wrapping option was toggled */ 00822 full_update|= drawcache->lheight != st->lheight; /* word-wrapping option was toggled */ 00823 full_update|= drawcache->cwidth != st->cwidth; /* word-wrapping option was toggled */ 00824 full_update|= strncmp(drawcache->text_id, txt->id.name, MAX_ID_NAME); /* text datablock was changed */ 00825 00826 if(st->wordwrap) { 00827 /* update line heights */ 00828 if(full_update || !drawcache->line_height) { 00829 drawcache->valid_head = 0; 00830 drawcache->valid_tail = 0; 00831 drawcache->update_flag = 1; 00832 } 00833 00834 if(drawcache->update_flag) { 00835 TextLine *line= st->text->lines.first; 00836 int lineno= 0, size, lines_count; 00837 int *fp= drawcache->line_height, *new_tail, *old_tail; 00838 00839 nlines= BLI_countlist(&txt->lines); 00840 size= sizeof(int)*nlines; 00841 00842 if(fp) fp= MEM_reallocN(fp, size); 00843 else fp= MEM_callocN(size, "text drawcache line_height"); 00844 00845 drawcache->valid_tail= drawcache->valid_head= 0; 00846 old_tail= fp + drawcache->nlines - drawcache->valid_tail; 00847 new_tail= fp + nlines - drawcache->valid_tail; 00848 memmove(new_tail, old_tail, drawcache->valid_tail); 00849 00850 drawcache->total_lines= 0; 00851 00852 if(st->showlinenrs) 00853 st->linenrs_tot= (int)floor(log10((float)nlines)) + 1; 00854 00855 while(line) { 00856 if(drawcache->valid_head) { /* we're inside valid head lines */ 00857 lines_count= fp[lineno]; 00858 drawcache->valid_head--; 00859 } else if (lineno > new_tail - fp) { /* we-re inside valid tail lines */ 00860 lines_count= fp[lineno]; 00861 } else { 00862 lines_count= text_get_visible_lines(st, ar, line->line); 00863 } 00864 00865 fp[lineno]= lines_count; 00866 00867 line= line->next; 00868 lineno++; 00869 drawcache->total_lines+= lines_count; 00870 } 00871 00872 drawcache->line_height= fp; 00873 } 00874 } else { 00875 if(drawcache->line_height) { 00876 MEM_freeN(drawcache->line_height); 00877 drawcache->line_height= NULL; 00878 } 00879 00880 if(full_update || drawcache->update_flag) { 00881 nlines= BLI_countlist(&txt->lines); 00882 00883 if(st->showlinenrs) 00884 st->linenrs_tot= (int)floor(log10((float)nlines)) + 1; 00885 } 00886 00887 drawcache->total_lines= nlines; 00888 } 00889 00890 drawcache->nlines= nlines; 00891 00892 /* store settings */ 00893 drawcache->winx = ar->winx; 00894 drawcache->wordwrap = st->wordwrap; 00895 drawcache->lheight = st->lheight; 00896 drawcache->cwidth = st->cwidth; 00897 drawcache->showlinenrs = st->showlinenrs; 00898 drawcache->tabnumber = st->tabnumber; 00899 00900 strncpy(drawcache->text_id, txt->id.name, MAX_ID_NAME); 00901 00902 /* clear update flag */ 00903 drawcache->update_flag = 0; 00904 drawcache->valid_head = 0; 00905 drawcache->valid_tail = 0; 00906 } 00907 00908 void text_drawcache_tag_update(SpaceText *st, int full) 00909 { 00910 DrawCache *drawcache= (DrawCache *)st->drawcache; 00911 00912 if(drawcache) { 00913 Text *txt= st->text; 00914 00915 if(drawcache->update_flag) { 00916 /* happens when tagging update from space listener */ 00917 /* should do nothing to prevent locally tagged cache be fully recalculated */ 00918 return; 00919 } 00920 00921 if(!full) { 00922 int sellno= BLI_findindex(&txt->lines, txt->sell); 00923 int curlno= BLI_findindex(&txt->lines, txt->curl); 00924 00925 if(curlno < sellno) { 00926 drawcache->valid_head= curlno; 00927 drawcache->valid_tail= drawcache->nlines - sellno - 1; 00928 } else { 00929 drawcache->valid_head= sellno; 00930 drawcache->valid_tail= drawcache->nlines - curlno - 1; 00931 } 00932 00933 /* quick cache recalculation is also used in delete operator, 00934 which could merge lines which are adjusent to current selection lines 00935 expand recalculate area to this lines */ 00936 if(drawcache->valid_head>0) drawcache->valid_head--; 00937 if(drawcache->valid_tail>0) drawcache->valid_tail--; 00938 } else { 00939 drawcache->valid_head= 0; 00940 drawcache->valid_tail= 0; 00941 } 00942 00943 drawcache->update_flag= 1; 00944 } 00945 } 00946 00947 void text_free_caches(SpaceText *st) 00948 { 00949 DrawCache *drawcache= (DrawCache *)st->drawcache; 00950 00951 if(drawcache) { 00952 if(drawcache->line_height) 00953 MEM_freeN(drawcache->line_height); 00954 00955 MEM_freeN(drawcache); 00956 } 00957 } 00958 00959 /************************ word-wrap utilities *****************************/ 00960 00961 /* cache should be updated in caller */ 00962 static int text_get_visible_lines_no(SpaceText *st, int lineno) 00963 { 00964 DrawCache *drawcache= (DrawCache *)st->drawcache; 00965 00966 return drawcache->line_height[lineno]; 00967 } 00968 00969 int text_get_visible_lines(SpaceText *st, ARegion *ar, const char *str) 00970 { 00971 int i, j, start, end, max, lines, chars; 00972 char ch; 00973 00974 max= wrap_width(st, ar); 00975 lines= 1; 00976 start= 0; 00977 end= max; 00978 for(i= 0, j= 0; str[j] != '\0'; j++) { 00979 /* Mimic replacement of tabs */ 00980 ch= str[j]; 00981 if(ch=='\t') { 00982 chars= st->tabnumber-i%st->tabnumber; 00983 ch= ' '; 00984 } 00985 else chars= 1; 00986 00987 while(chars--) { 00988 if(i-start >= max) { 00989 lines++; 00990 start= end; 00991 end += max; 00992 } 00993 else if(ch==' ' || ch=='-') { 00994 end= i+1; 00995 } 00996 00997 i++; 00998 } 00999 } 01000 01001 return lines; 01002 } 01003 01004 int text_get_span_wrap(SpaceText *st, ARegion *ar, TextLine *from, TextLine *to) 01005 { 01006 if(st->wordwrap) { 01007 int ret=0; 01008 TextLine *tmp= from; 01009 01010 /* Look forwards */ 01011 while (tmp) { 01012 if (tmp == to) return ret; 01013 ret+= text_get_visible_lines(st, ar, tmp->line); 01014 tmp= tmp->next; 01015 } 01016 01017 return ret; 01018 } else return txt_get_span(from, to); 01019 } 01020 01021 int text_get_total_lines(SpaceText *st, ARegion *ar) 01022 { 01023 DrawCache *drawcache; 01024 01025 text_update_drawcache(st, ar); 01026 drawcache= (DrawCache *)st->drawcache; 01027 01028 return drawcache->total_lines; 01029 } 01030 01031 /* Move pointer to first visible line (top) */ 01032 static TextLine *first_visible_line(SpaceText *st, ARegion *ar, int *wrap_top) 01033 { 01034 Text *text= st->text; 01035 TextLine* pline= text->lines.first; 01036 int i= st->top, lineno= 0; 01037 01038 text_update_drawcache(st, ar); 01039 01040 if(wrap_top) *wrap_top= 0; 01041 01042 if(st->wordwrap) { 01043 while(i>0 && pline) { 01044 int lines= text_get_visible_lines_no(st, lineno); 01045 01046 if (i-lines<0) { 01047 if(wrap_top) *wrap_top= i; 01048 break; 01049 } else { 01050 pline= pline->next; 01051 i-= lines; 01052 lineno++; 01053 } 01054 } 01055 } else { 01056 for(i=st->top; pline->next && i>0; i--) 01057 pline= pline->next; 01058 } 01059 01060 return pline; 01061 } 01062 01063 /************************ draw scrollbar *****************************/ 01064 01065 static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll, rcti *back) 01066 { 01067 int lhlstart, lhlend, ltexth, sell_off, curl_off; 01068 short barheight, barstart, hlstart, hlend, blank_lines; 01069 short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff; 01070 01071 pix_top_margin = 8; 01072 pix_bottom_margin = 4; 01073 pix_available = ar->winy - pix_top_margin - pix_bottom_margin; 01074 ltexth= text_get_total_lines(st, ar); 01075 blank_lines = st->viewlines / 2; 01076 01077 /* nicer code: use scroll rect for entire bar */ 01078 back->xmin= ar->winx -18; 01079 back->xmax= ar->winx; 01080 back->ymin= 0; 01081 back->ymax= ar->winy; 01082 01083 scroll->xmin= ar->winx - 17; 01084 scroll->xmax= ar->winx - 5; 01085 scroll->ymin= 4; 01086 scroll->ymax= 4+pix_available; 01087 01088 /* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */ 01089 if(ltexth + blank_lines < st->top + st->viewlines) { 01090 blank_lines = st->top + st->viewlines - ltexth; 01091 } 01092 01093 ltexth += blank_lines; 01094 01095 barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0; 01096 pix_bardiff = 0; 01097 if(barheight < 20) { 01098 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */ 01099 barheight = 20; 01100 } 01101 barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0; 01102 01103 st->txtbar= *scroll; 01104 st->txtbar.ymax -= barstart; 01105 st->txtbar.ymin = st->txtbar.ymax - barheight; 01106 01107 CLAMP(st->txtbar.ymin, pix_bottom_margin, ar->winy - pix_top_margin); 01108 CLAMP(st->txtbar.ymax, pix_bottom_margin, ar->winy - pix_top_margin); 01109 01110 st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0; 01111 if(st->pix_per_line < 0.1f) st->pix_per_line=0.1f; 01112 01113 curl_off= text_get_span_wrap(st, ar, st->text->lines.first, st->text->curl); 01114 sell_off= text_get_span_wrap(st, ar, st->text->lines.first, st->text->sell); 01115 lhlstart = MIN2(curl_off, sell_off); 01116 lhlend = MAX2(curl_off, sell_off); 01117 01118 if(ltexth > 0) { 01119 hlstart = (lhlstart * pix_available)/ltexth; 01120 hlend = (lhlend * pix_available)/ltexth; 01121 01122 /* the scrollbar is non-linear sized */ 01123 if(pix_bardiff > 0) { 01124 /* the start of the highlight is in the current viewport */ 01125 if(ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) { 01126 /* speed the progresion of the start of the highlight through the scrollbar */ 01127 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines); 01128 } 01129 else if(lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) { 01130 /* push hl start down */ 01131 hlstart = barstart + barheight; 01132 } 01133 else if(lhlend > st->top && lhlstart < st->top && hlstart > barstart) { 01134 /*fill out start */ 01135 hlstart = barstart; 01136 } 01137 01138 if(hlend <= hlstart) { 01139 hlend = hlstart + 2; 01140 } 01141 01142 /* the end of the highlight is in the current viewport */ 01143 if(ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) { 01144 /* speed the progresion of the end of the highlight through the scrollbar */ 01145 hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines); 01146 } 01147 else if(lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) { 01148 /* push hl end up */ 01149 hlend = barstart; 01150 } 01151 else if(lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) { 01152 /* fill out end */ 01153 hlend = barstart + barheight; 01154 } 01155 01156 if(hlend <= hlstart) { 01157 hlstart = hlend - 2; 01158 } 01159 } 01160 } 01161 else { 01162 hlstart = 0; 01163 hlend = 0; 01164 } 01165 01166 if(hlend - hlstart < 2) { 01167 hlend = hlstart + 2; 01168 } 01169 01170 st->txtscroll= *scroll; 01171 st->txtscroll.ymax= ar->winy - pix_top_margin - hlstart; 01172 st->txtscroll.ymin= ar->winy - pix_top_margin - hlend; 01173 01174 CLAMP(st->txtscroll.ymin, pix_bottom_margin, ar->winy - pix_top_margin); 01175 CLAMP(st->txtscroll.ymax, pix_bottom_margin, ar->winy - pix_top_margin); 01176 } 01177 01178 static void draw_textscroll(SpaceText *st, rcti *scroll, rcti *back) 01179 { 01180 bTheme *btheme= U.themes.first; 01181 uiWidgetColors wcol= btheme->tui.wcol_scroll; 01182 unsigned char col[4]; 01183 float rad; 01184 01185 UI_ThemeColor(TH_BACK); 01186 glRecti(back->xmin, back->ymin, back->xmax, back->ymax); 01187 01188 uiWidgetScrollDraw(&wcol, scroll, &st->txtbar, (st->flags & ST_SCROLL_SELECT)?UI_SCROLL_PRESSED:0); 01189 01190 uiSetRoundBox(15); 01191 rad= 0.4f*MIN2(st->txtscroll.xmax - st->txtscroll.xmin, st->txtscroll.ymax - st->txtscroll.ymin); 01192 UI_GetThemeColor3ubv(TH_HILITE, col); 01193 col[3]= 48; 01194 glColor4ubv(col); 01195 glEnable(GL_BLEND); 01196 uiRoundBox(st->txtscroll.xmin+1, st->txtscroll.ymin, st->txtscroll.xmax-1, st->txtscroll.ymax, rad); 01197 glDisable(GL_BLEND); 01198 } 01199 01200 /************************** draw markers **************************/ 01201 01202 static void draw_markers(SpaceText *st, ARegion *ar) 01203 { 01204 Text *text= st->text; 01205 TextMarker *marker, *next; 01206 TextLine *top, *line; 01207 int offl, offc, i, x1, x2, y1, y2, x, y; 01208 int topi, topy; 01209 01210 /* Move pointer to first visible line (top) */ 01211 top= first_visible_line(st, ar, NULL); 01212 topi= BLI_findindex(&text->lines, top); 01213 01214 topy= txt_get_span(text->lines.first, top); 01215 01216 for(marker= text->markers.first; marker; marker= next) { 01217 next= marker->next; 01218 01219 /* invisible line (before top) */ 01220 if(marker->lineno<topi) continue; 01221 01222 line= BLI_findlink(&text->lines, marker->lineno); 01223 01224 /* Remove broken markers */ 01225 if(marker->end>line->len || marker->start>marker->end) { 01226 BLI_freelinkN(&text->markers, marker); 01227 continue; 01228 } 01229 01230 wrap_offset(st, ar, line, marker->start, &offl, &offc); 01231 y1 = txt_get_span(top, line) - st->top + offl + topy; 01232 x1 = text_get_char_pos(st, line->line, marker->start) - st->left + offc; 01233 01234 wrap_offset(st, ar, line, marker->end, &offl, &offc); 01235 y2 = txt_get_span(top, line) - st->top + offl + topy; 01236 x2 = text_get_char_pos(st, line->line, marker->end) - st->left + offc; 01237 01238 /* invisible part of line (before top, after last visible line) */ 01239 if(y2 < 0 || y1 > st->top+st->viewlines) continue; 01240 01241 glColor3ubv(marker->color); 01242 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET; 01243 y= ar->winy-3; 01244 01245 if(y1==y2) { 01246 y -= y1*st->lheight; 01247 glBegin(GL_LINE_LOOP); 01248 glVertex2i(x+x2*st->cwidth+1, y); 01249 glVertex2i(x+x1*st->cwidth-2, y); 01250 glVertex2i(x+x1*st->cwidth-2, y-st->lheight); 01251 glVertex2i(x+x2*st->cwidth+1, y-st->lheight); 01252 glEnd(); 01253 } 01254 else { 01255 y -= y1*st->lheight; 01256 glBegin(GL_LINE_STRIP); 01257 glVertex2i(ar->winx, y); 01258 glVertex2i(x+x1*st->cwidth-2, y); 01259 glVertex2i(x+x1*st->cwidth-2, y-st->lheight); 01260 glVertex2i(ar->winx, y-st->lheight); 01261 glEnd(); 01262 y-=st->lheight; 01263 01264 for(i=y1+1; i<y2; i++) { 01265 glBegin(GL_LINES); 01266 glVertex2i(x, y); 01267 glVertex2i(ar->winx, y); 01268 glVertex2i(x, y-st->lheight); 01269 glVertex2i(ar->winx, y-st->lheight); 01270 glEnd(); 01271 y-=st->lheight; 01272 } 01273 01274 glBegin(GL_LINE_STRIP); 01275 glVertex2i(x, y); 01276 glVertex2i(x+x2*st->cwidth+1, y); 01277 glVertex2i(x+x2*st->cwidth+1, y-st->lheight); 01278 glVertex2i(x, y-st->lheight); 01279 glEnd(); 01280 } 01281 } 01282 } 01283 01284 /*********************** draw documentation *******************************/ 01285 01286 static void draw_documentation(SpaceText *st, ARegion *ar) 01287 { 01288 TextLine *tmp; 01289 char *docs, buf[DOC_WIDTH+1], *p; 01290 int i, br, lines; 01291 int boxw, boxh, l, x, y, top; 01292 01293 if(!st || !st->text) return; 01294 if(!texttool_text_is_active(st->text)) return; 01295 01296 docs = texttool_docs_get(); 01297 01298 if(!docs) return; 01299 01300 /* Count the visible lines to the cursor */ 01301 for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++); 01302 if(l<0) return; 01303 01304 if(st->showlinenrs) { 01305 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4; 01306 } 01307 else { 01308 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4; 01309 } 01310 if(texttool_suggest_first()) { 01311 x += SUGG_LIST_WIDTH*st->cwidth + 50; 01312 } 01313 01314 top= y= ar->winy - st->lheight*l - 2; 01315 boxw= DOC_WIDTH*st->cwidth + 20; 01316 boxh= (DOC_HEIGHT+1)*st->lheight; 01317 01318 /* Draw panel */ 01319 UI_ThemeColor(TH_BACK); 01320 glRecti(x, y, x+boxw, y-boxh); 01321 UI_ThemeColor(TH_SHADE1); 01322 glBegin(GL_LINE_LOOP); 01323 glVertex2i(x, y); 01324 glVertex2i(x+boxw, y); 01325 glVertex2i(x+boxw, y-boxh); 01326 glVertex2i(x, y-boxh); 01327 glEnd(); 01328 glBegin(GL_LINE_LOOP); 01329 glVertex2i(x+boxw-10, y-7); 01330 glVertex2i(x+boxw-4, y-7); 01331 glVertex2i(x+boxw-7, y-2); 01332 glEnd(); 01333 glBegin(GL_LINE_LOOP); 01334 glVertex2i(x+boxw-10, y-boxh+7); 01335 glVertex2i(x+boxw-4, y-boxh+7); 01336 glVertex2i(x+boxw-7, y-boxh+2); 01337 glEnd(); 01338 UI_ThemeColor(TH_TEXT); 01339 01340 i= 0; br= DOC_WIDTH; lines= 0; // XXX -doc_scroll; 01341 for(p=docs; *p; p++) { 01342 if(*p == '\r' && *(++p) != '\n') *(--p)= '\n'; /* Fix line endings */ 01343 if(*p == ' ' || *p == '\t') 01344 br= i; 01345 else if(*p == '\n') { 01346 buf[i]= '\0'; 01347 if(lines>=0) { 01348 y -= st->lheight; 01349 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL); 01350 } 01351 i= 0; br= DOC_WIDTH; lines++; 01352 } 01353 buf[i++]= *p; 01354 if(i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */ 01355 buf[br]= '\0'; 01356 if(lines>=0) { 01357 y -= st->lheight; 01358 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL); 01359 } 01360 p -= i-br-1; /* Rewind pointer to last break */ 01361 i= 0; br= DOC_WIDTH; lines++; 01362 } 01363 if(lines >= DOC_HEIGHT) break; 01364 } 01365 01366 if(0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) { 01367 // XXX doc_scroll--; 01368 draw_documentation(st, ar); 01369 } 01370 } 01371 01372 /*********************** draw suggestion list *******************************/ 01373 01374 static void draw_suggestion_list(SpaceText *st, ARegion *ar) 01375 { 01376 SuggItem *item, *first, *last, *sel; 01377 TextLine *tmp; 01378 char str[SUGG_LIST_WIDTH+1]; 01379 int w, boxw=0, boxh, i, l, x, y, b, *top; 01380 01381 if(!st || !st->text) return; 01382 if(!texttool_text_is_active(st->text)) return; 01383 01384 first = texttool_suggest_first(); 01385 last = texttool_suggest_last(); 01386 01387 if(!first || !last) return; 01388 01389 text_pop_suggest_list(); 01390 sel = texttool_suggest_selected(); 01391 top = texttool_suggest_top(); 01392 01393 /* Count the visible lines to the cursor */ 01394 for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++); 01395 if(l<0) return; 01396 01397 if(st->showlinenrs) { 01398 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4; 01399 } 01400 else { 01401 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4; 01402 } 01403 y = ar->winy - st->lheight*l - 2; 01404 01405 boxw = SUGG_LIST_WIDTH*st->cwidth + 20; 01406 boxh = SUGG_LIST_SIZE*st->lheight + 8; 01407 01408 UI_ThemeColor(TH_SHADE1); 01409 glRecti(x-1, y+1, x+boxw+1, y-boxh-1); 01410 UI_ThemeColor(TH_BACK); 01411 glRecti(x, y, x+boxw, y-boxh); 01412 01413 /* Set the top 'item' of the visible list */ 01414 for(i=0, item=first; i<*top && item->next; i++, item=item->next); 01415 01416 for(i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) { 01417 01418 y -= st->lheight; 01419 01420 strncpy(str, item->name, SUGG_LIST_WIDTH); 01421 str[SUGG_LIST_WIDTH] = '\0'; 01422 01423 w = text_font_width(st, str); 01424 01425 if(item == sel) { 01426 UI_ThemeColor(TH_SHADE2); 01427 glRecti(x+16, y-3, x+16+w, y+st->lheight-3); 01428 } 01429 b=1; /* b=1 color block, text is default. b=0 no block, color text */ 01430 switch (item->type) { 01431 case 'k': UI_ThemeColor(TH_SYNTAX_B); b=0; break; 01432 case 'm': UI_ThemeColor(TH_TEXT); break; 01433 case 'f': UI_ThemeColor(TH_SYNTAX_L); break; 01434 case 'v': UI_ThemeColor(TH_SYNTAX_N); break; 01435 case '?': UI_ThemeColor(TH_TEXT); b=0; break; 01436 } 01437 if(b) { 01438 glRecti(x+8, y+2, x+11, y+5); 01439 UI_ThemeColor(TH_TEXT); 01440 } 01441 text_draw(st, str, 0, 0, 1, x+16, y-1, NULL); 01442 01443 if(item == last) break; 01444 } 01445 } 01446 01447 /*********************** draw cursor ************************/ 01448 01449 static void draw_cursor(SpaceText *st, ARegion *ar) 01450 { 01451 Text *text= st->text; 01452 int vcurl, vcurc, vsell, vselc, hidden=0; 01453 int x, y, w, i; 01454 01455 /* Draw the selection */ 01456 if(text->curl!=text->sell || text->curc!=text->selc) { 01457 int offl, offc; 01458 /* Convert all to view space character coordinates */ 01459 wrap_offset(st, ar, text->curl, text->curc, &offl, &offc); 01460 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl; 01461 vcurc = text_get_char_pos(st, text->curl->line, text->curc) - st->left + offc; 01462 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc); 01463 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl; 01464 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc; 01465 01466 if(vcurc<0) vcurc=0; 01467 if(vselc<0) vselc=0, hidden=1; 01468 01469 UI_ThemeColor(TH_SHADE2); 01470 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET; 01471 y= ar->winy-2; 01472 01473 if(vcurl==vsell) { 01474 y -= vcurl*st->lheight; 01475 if(vcurc < vselc) 01476 glRecti(x+vcurc*st->cwidth-1, y, x+vselc*st->cwidth, y-st->lheight); 01477 else 01478 glRecti(x+vselc*st->cwidth-1, y, x+vcurc*st->cwidth, y-st->lheight); 01479 } 01480 else { 01481 int froml, fromc, tol, toc; 01482 01483 if(vcurl < vsell) { 01484 froml= vcurl; tol= vsell; 01485 fromc= vcurc; toc= vselc; 01486 } 01487 else { 01488 froml= vsell; tol= vcurl; 01489 fromc= vselc; toc= vcurc; 01490 } 01491 01492 y -= froml*st->lheight; 01493 glRecti(x+fromc*st->cwidth-1, y, ar->winx, y-st->lheight); y-=st->lheight; 01494 for(i=froml+1; i<tol; i++) 01495 glRecti(x-4, y, ar->winx, y-st->lheight), y-=st->lheight; 01496 01497 glRecti(x-4, y, x+toc*st->cwidth, y-st->lheight); y-=st->lheight; 01498 01499 (void)y; 01500 } 01501 } 01502 else { 01503 int offl, offc; 01504 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc); 01505 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl; 01506 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc; 01507 01508 if(vselc<0) { 01509 vselc= 0; 01510 hidden= 1; 01511 } 01512 } 01513 01514 if(st->line_hlight) { 01515 int x1, x2, y1, y2; 01516 01517 if(st->wordwrap) { 01518 int visible_lines = text_get_visible_lines(st, ar, text->sell->line); 01519 int offl, offc; 01520 01521 wrap_offset_in_line(st, ar, text->sell, text->selc, &offl, &offc); 01522 01523 y1= ar->winy-2 - (vsell-offl)*st->lheight; 01524 y2= y1-st->lheight*visible_lines+1; 01525 } else { 01526 y1= ar->winy-2 - vsell*st->lheight; 01527 y2= y1-st->lheight+1; 01528 } 01529 01530 if(!(y1<0 || y2 > ar->winy)) { /* check we need to draw */ 01531 x1= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET; 01532 x2= x1 + ar->winx; 01533 01534 glColor4ub(255, 255, 255, 32); 01535 01536 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 01537 glEnable(GL_BLEND); 01538 glRecti(x1-4, y1, x2, y2); 01539 glDisable(GL_BLEND); 01540 } 01541 } 01542 01543 if(!hidden) { 01544 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */ 01545 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET; 01546 x += vselc*st->cwidth; 01547 y= ar->winy-2 - vsell*st->lheight; 01548 01549 if(st->overwrite) { 01550 char ch= text->sell->line[text->selc]; 01551 01552 w= st->cwidth; 01553 if(ch=='\t') w*= st->tabnumber-(vselc+st->left)%st->tabnumber; 01554 01555 UI_ThemeColor(TH_HILITE); 01556 glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1); 01557 } 01558 else { 01559 UI_ThemeColor(TH_HILITE); 01560 glRecti(x-1, y, x+1, y-st->lheight); 01561 } 01562 } 01563 } 01564 01565 /******************* draw matching brackets *********************/ 01566 01567 static void draw_brackets(SpaceText *st, ARegion *ar) 01568 { 01569 TextLine *startl, *endl, *linep; 01570 Text *text = st->text; 01571 int b, c, startc, endc, find, stack; 01572 int viewc, viewl, offl, offc, x, y; 01573 char ch; 01574 01575 // showsyntax must be on or else the format string will be null 01576 if(!text->curl || !st->showsyntax) return; 01577 01578 startl= text->curl; 01579 startc= text->curc; 01580 b= text_check_bracket(startl->line[startc]); 01581 if(b==0 && startc>0) b = text_check_bracket(startl->line[--startc]); 01582 if(b==0) return; 01583 01584 linep= startl; 01585 c= startc; 01586 endl= NULL; 01587 endc= -1; 01588 find= -b; 01589 stack= 0; 01590 01591 /* Dont highlight backets if syntax HL is off or bracket in string or comment. */ 01592 if(!linep->format || linep->format[c] == 'l' || linep->format[c] == '#') 01593 return; 01594 01595 if(b>0) { 01596 /* opening bracket, search forward for close */ 01597 c++; 01598 while(linep) { 01599 while(c<linep->len) { 01600 if(linep->format && linep->format[c] != 'l' && linep->format[c] != '#') { 01601 b= text_check_bracket(linep->line[c]); 01602 if(b==find) { 01603 if(stack==0) { 01604 endl= linep; 01605 endc= c; 01606 break; 01607 } 01608 stack--; 01609 } 01610 else if(b==-find) { 01611 stack++; 01612 } 01613 } 01614 c++; 01615 } 01616 if(endl) break; 01617 linep= linep->next; 01618 c= 0; 01619 } 01620 } 01621 else { 01622 /* closing bracket, search backward for open */ 01623 c--; 01624 while(linep) { 01625 while(c>=0) { 01626 if(linep->format && linep->format[c] != 'l' && linep->format[c] != '#') { 01627 b= text_check_bracket(linep->line[c]); 01628 if(b==find) { 01629 if(stack==0) { 01630 endl= linep; 01631 endc= c; 01632 break; 01633 } 01634 stack--; 01635 } 01636 else if(b==-find) { 01637 stack++; 01638 } 01639 } 01640 c--; 01641 } 01642 if(endl) break; 01643 linep= linep->prev; 01644 if(linep) c= linep->len-1; 01645 } 01646 } 01647 01648 if(!endl || endc==-1) 01649 return; 01650 01651 UI_ThemeColor(TH_HILITE); 01652 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET; 01653 y= ar->winy - st->lheight; 01654 01655 /* draw opening bracket */ 01656 ch= startl->line[startc]; 01657 wrap_offset(st, ar, startl, startc, &offl, &offc); 01658 viewc= text_get_char_pos(st, startl->line, startc) - st->left + offc; 01659 01660 if(viewc >= 0){ 01661 viewl= txt_get_span(text->lines.first, startl) - st->top + offl; 01662 01663 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch); 01664 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch); 01665 } 01666 01667 /* draw closing bracket */ 01668 ch= endl->line[endc]; 01669 wrap_offset(st, ar, endl, endc, &offl, &offc); 01670 viewc= text_get_char_pos(st, endl->line, endc) - st->left + offc; 01671 01672 if(viewc >= 0) { 01673 viewl= txt_get_span(text->lines.first, endl) - st->top + offl; 01674 01675 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch); 01676 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch); 01677 } 01678 } 01679 01680 /*********************** main area drawing *************************/ 01681 01682 void draw_text_main(SpaceText *st, ARegion *ar) 01683 { 01684 Text *text= st->text; 01685 TextLine *tmp; 01686 rcti scroll, back; 01687 char linenr[12]; 01688 int i, x, y, winx, linecount= 0, lineno= 0; 01689 int wraplinecount= 0, wrap_skip= 0; 01690 01691 /* if no text, nothing to do */ 01692 if(!text) 01693 return; 01694 01695 text_update_drawcache(st, ar); 01696 01697 /* make sure all the positional pointers exist */ 01698 if(!text->curl || !text->sell || !text->lines.first || !text->lines.last) 01699 txt_clean_text(text); 01700 01701 if(st->lheight) st->viewlines= (int)ar->winy/st->lheight; 01702 else st->viewlines= 0; 01703 01704 /* update rects for scroll */ 01705 calc_text_rcts(st, ar, &scroll, &back); /* scroll will hold the entire bar size */ 01706 01707 /* update syntax formatting if needed */ 01708 tmp= text->lines.first; 01709 lineno= 0; 01710 for(i= 0; i<st->top && tmp; i++) { 01711 if(st->showsyntax && !tmp->format) 01712 txt_format_line(st, tmp, 0); 01713 01714 if(st->wordwrap) { 01715 int lines= text_get_visible_lines_no(st, lineno); 01716 01717 if (wraplinecount+lines>st->top) { 01718 wrap_skip= st->top-wraplinecount; 01719 break; 01720 } else { 01721 wraplinecount+= lines; 01722 tmp= tmp->next; 01723 linecount++; 01724 } 01725 } else { 01726 tmp= tmp->next; 01727 linecount++; 01728 } 01729 01730 lineno++; 01731 } 01732 01733 text_font_begin(st); 01734 st->cwidth= BLF_fixed_width(mono); 01735 st->cwidth= MAX2(st->cwidth, 1); 01736 01737 /* draw line numbers background */ 01738 if(st->showlinenrs) { 01739 x= TXT_OFFSET + TEXTXLOC; 01740 01741 UI_ThemeColor(TH_GRID); 01742 glRecti((TXT_OFFSET-12), 0, (TXT_OFFSET-5) + TEXTXLOC, ar->winy - 2); 01743 } 01744 else { 01745 st->linenrs_tot= 0; /* not used */ 01746 x= TXT_OFFSET; 01747 } 01748 y= ar->winy-st->lheight; 01749 winx= ar->winx - TXT_SCROLL_WIDTH; 01750 01751 /* draw cursor */ 01752 draw_cursor(st, ar); 01753 01754 /* draw the text */ 01755 UI_ThemeColor(TH_TEXT); 01756 01757 for(i=0; y>0 && i<st->viewlines && tmp; i++, tmp= tmp->next) { 01758 if(st->showsyntax && !tmp->format) 01759 txt_format_line(st, tmp, 0); 01760 01761 if(st->showlinenrs && !wrap_skip) { 01762 /* draw line number */ 01763 if(tmp == text->curl) 01764 UI_ThemeColor(TH_HILITE); 01765 else 01766 UI_ThemeColor(TH_TEXT); 01767 01768 sprintf(linenr, "%*d", st->linenrs_tot, i + linecount + 1); 01769 /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */ 01770 text_font_draw(st, TXT_OFFSET - 7, y, linenr); 01771 01772 UI_ThemeColor(TH_TEXT); 01773 } 01774 01775 if(st->wordwrap) { 01776 /* draw word wrapped text */ 01777 int lines = text_draw_wrapped(st, tmp->line, x, y, winx-x, tmp->format, wrap_skip); 01778 y -= lines*st->lheight; 01779 } 01780 else { 01781 /* draw unwrapped text */ 01782 text_draw(st, tmp->line, st->left, ar->winx/st->cwidth, 1, x, y, tmp->format); 01783 y -= st->lheight; 01784 } 01785 01786 wrap_skip= 0; 01787 } 01788 01789 if(st->flags&ST_SHOW_MARGIN) { 01790 UI_ThemeColor(TH_HILITE); 01791 01792 glBegin(GL_LINES); 01793 glVertex2i(x+st->cwidth*st->margin_column, 0); 01794 glVertex2i(x+st->cwidth*st->margin_column, ar->winy - 2); 01795 glEnd(); 01796 } 01797 01798 /* draw other stuff */ 01799 draw_brackets(st, ar); 01800 draw_markers(st, ar); 01801 glTranslatef(0.375f, 0.375f, 0.0f); /* XXX scroll requires exact pixel space */ 01802 draw_textscroll(st, &scroll, &back); 01803 draw_documentation(st, ar); 01804 draw_suggestion_list(st, ar); 01805 01806 text_font_end(st); 01807 } 01808 01809 /************************** update ***************************/ 01810 01811 void text_update_character_width(SpaceText *st) 01812 { 01813 text_font_begin(st); 01814 st->cwidth= BLF_fixed_width(mono); 01815 st->cwidth= MAX2(st->cwidth, 1); 01816 text_font_end(st); 01817 } 01818 01819 /* Moves the view to the cursor location, 01820 also used to make sure the view isnt outside the file */ 01821 void text_update_cursor_moved(bContext *C) 01822 { 01823 ScrArea *sa= CTX_wm_area(C); 01824 SpaceText *st= CTX_wm_space_text(C); 01825 Text *text; 01826 ARegion *ar; 01827 int i, x, winx= 0; 01828 01829 if(ELEM3(NULL, st, st->text, st->text->curl)) return; 01830 01831 text= st->text; 01832 01833 for(ar=sa->regionbase.first; ar; ar= ar->next) 01834 if(ar->regiontype==RGN_TYPE_WINDOW) 01835 winx= ar->winx; 01836 01837 winx -= TXT_SCROLL_WIDTH; 01838 01839 text_update_character_width(st); 01840 01841 i= txt_get_span(text->lines.first, text->sell); 01842 if(st->wordwrap) { 01843 int offl, offc; 01844 wrap_offset(st, CTX_wm_region(C), text->sell, text->selc, &offl, &offc); 01845 i+= offl; 01846 } 01847 01848 if(st->top+st->viewlines <= i || st->top > i) 01849 st->top= i - st->viewlines/2; 01850 01851 if(st->wordwrap) { 01852 st->left= 0; 01853 } 01854 else { 01855 x= text_draw(st, text->sell->line, st->left, text->selc, 0, 0, 0, NULL); 01856 01857 if(x==0 || x>winx) 01858 st->left= text->curc-0.5*winx/st->cwidth; 01859 } 01860 01861 if(st->top < 0) st->top= 0; 01862 if(st->left <0) st->left= 0; 01863 } 01864