Blender V2.61 - r43446

text_draw.c

Go to the documentation of this file.
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 }