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