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