use BLI_strncpy and BLI_snprintf when the size of the string is known.
[blender-staging.git] / source / blender / editors / space_text / text_draw.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16  *
17  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
18  * All rights reserved.
19  *
20  * The Original Code is: all of this file.
21  *
22  * Contributor(s): none yet.
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_text/text_draw.c
28  *  \ingroup sptext
29  */
30
31
32 #include <math.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/stat.h>
36
37 #include "MEM_guardedalloc.h"
38
39 #include "BLF_api.h"
40
41 #include "BLI_blenlib.h"
42 #include "BLI_utildefines.h"
43
44 #include "DNA_text_types.h"
45 #include "DNA_space_types.h"
46 #include "DNA_screen_types.h"
47 #include "DNA_userdef_types.h"
48
49 #include "BKE_context.h"
50 #include "BKE_suggestions.h"
51 #include "BKE_text.h"
52
53
54 #include "BIF_gl.h"
55
56 #include "ED_datafiles.h"
57 #include "UI_interface.h"
58 #include "UI_resources.h"
59
60 #include "text_intern.h"
61
62 /******************** text font drawing ******************/
63 // XXX, fixme
64 #define mono blf_mono_font
65
66 static void text_font_begin(SpaceText *st)
67 {
68         BLF_size(mono, st->lheight, 72);
69 }
70
71 static void text_font_end(SpaceText *UNUSED(st))
72 {
73 }
74
75 static int text_font_draw(SpaceText *UNUSED(st), int x, int y, char *str)
76 {
77         BLF_position(mono, x, y, 0);
78         BLF_draw(mono, str, BLF_DRAW_STR_DUMMY_MAX);
79
80         return BLF_width(mono, str);
81 }
82
83 static int text_font_draw_character(SpaceText *st, int x, int y, char c)
84 {
85         char str[2];
86         str[0]= c;
87         str[1]= '\0';
88
89         BLF_position(mono, x, y, 0);
90         BLF_draw(mono, str, 1);
91
92         return st->cwidth;
93 }
94
95 int text_font_width(SpaceText *UNUSED(st), const char *str)
96 {
97         return BLF_width(mono, str);
98 }
99
100 /****************** flatten string **********************/
101
102 static void flatten_string_append(FlattenString *fs, char c, int accum) 
103 {
104         if(fs->pos>=fs->len && fs->pos>=sizeof(fs->fixedbuf)-1) {
105                 char *nbuf; int *naccum;
106                 if(fs->len) fs->len*= 2;
107                 else fs->len= sizeof(fs->fixedbuf) * 2;
108
109                 nbuf= MEM_callocN(sizeof(*fs->buf)*fs->len, "fs->buf");
110                 naccum= MEM_callocN(sizeof(*fs->accum)*fs->len, "fs->accum");
111
112                 memcpy(nbuf, fs->buf, fs->pos * sizeof(*fs->buf));
113                 memcpy(naccum, fs->accum, fs->pos * sizeof(*fs->accum));
114                 
115                 if(fs->buf != fs->fixedbuf) {
116                         MEM_freeN(fs->buf);
117                         MEM_freeN(fs->accum);
118                 }
119                 
120                 fs->buf= nbuf;
121                 fs->accum= naccum;
122         }
123         
124         fs->buf[fs->pos]= c;    
125         fs->accum[fs->pos]= accum;
126         
127         fs->pos++;
128 }
129
130 int flatten_string(SpaceText *st, FlattenString *fs, const char *in)
131 {
132         int r = 0, i = 0;
133
134         memset(fs, 0, sizeof(FlattenString));
135         fs->buf= fs->fixedbuf;
136         fs->accum= fs->fixedaccum;
137         
138         for(r=0, i=0; *in; r++, in++) {
139                 if(*in=='\t') {
140                         if(fs->pos && *(in-1)=='\t')
141                                 i= st->tabnumber;
142                         else if(st->tabnumber > 0)
143                                 i= st->tabnumber - (fs->pos%st->tabnumber);
144
145                         while(i--)
146                                 flatten_string_append(fs, ' ', r);
147                 }
148                 else
149                         flatten_string_append(fs, *in, r);
150         }
151
152         return fs->pos;
153 }
154
155 void flatten_string_free(FlattenString *fs)
156 {
157         if(fs->buf != fs->fixedbuf)
158                 MEM_freeN(fs->buf);
159         if(fs->accum != fs->fixedaccum)
160                 MEM_freeN(fs->accum);
161 }
162
163 /* Checks the specified source string for a Python built-in function name. This
164  name must start at the beginning of the source string and must be followed by
165  a non-identifier (see text_check_identifier(char)) or null character.
166  
167  If a built-in function is found, the length of the matching name is returned.
168  Otherwise, -1 is returned. */
169
170 static int find_builtinfunc(char *string)
171 {
172         int a, i;
173         char builtinfuncs[][9] = {"and", "as", "assert", "break", "class", "continue", "def",
174                                                                 "del", "elif", "else", "except", "exec", "finally",
175                                                                 "for", "from", "global", "if", "import", "in",
176                                                                 "is", "lambda", "not", "or", "pass", "print",
177                                                                 "raise", "return", "try", "while", "yield", "with"};
178
179         for(a=0; a < sizeof(builtinfuncs)/sizeof(builtinfuncs[0]); a++) {
180                 i = 0;
181                 while(1) {
182                         /* If we hit the end of a keyword... (eg. "def") */
183                         if(builtinfuncs[a][i]=='\0') {
184                                 /* If we still have identifier chars in the source (eg. "definate") */
185                                 if(text_check_identifier(string[i]))
186                                         i = -1; /* No match */
187                                 break; /* Next keyword if no match, otherwise we're done */
188                                 
189                         /* If chars mismatch, move on to next keyword */
190                         }
191                         else if(string[i]!=builtinfuncs[a][i]) {
192                                 i = -1;
193                                 break; /* Break inner loop, start next keyword */
194                         }
195                         i++;
196                 }
197                 if(i>0) break; /* If we have a match, we're done */
198         }
199         return i;
200 }
201
202 /* Checks the specified source string for a Python special name. This name must
203  start at the beginning of the source string and must be followed by a non-
204  identifier (see text_check_identifier(char)) or null character.
205  
206  If a special name is found, the length of the matching name is returned.
207  Otherwise, -1 is returned. */
208
209 static int find_specialvar(char *string) 
210 {
211         int i = 0;
212         /* Check for "def" */
213         if(string[0]=='d' && string[1]=='e' && string[2]=='f')
214                 i = 3;
215         /* Check for "class" */
216         else if(string[0]=='c' && string[1]=='l' && string[2]=='a' && string[3]=='s' && string[4]=='s')
217                 i = 5;
218         /* If next source char is an identifier (eg. 'i' in "definate") no match */
219         if(i==0 || text_check_identifier(string[i]))
220                 return -1;
221         return i;
222 }
223
224 static int find_decorator(char *string) 
225 {
226         if(string[0] == '@') {
227                 int i = 1;
228                 while(text_check_identifier(string[i])) {
229                         i++;
230                 }
231                 return i;
232         }
233         return -1;
234 }
235
236 static int find_bool(char *string) 
237 {
238         int i = 0;
239         /* Check for "False" */
240         if(string[0]=='F' && string[1]=='a' && string[2]=='l' && string[3]=='s' && string[4]=='e')
241                 i = 5;
242         /* Check for "True" */
243         else if(string[0]=='T' && string[1]=='r' && string[2]=='u' && string[3]=='e')
244                 i = 4;
245         /* Check for "None" */
246         else if(string[0]=='N' && string[1]=='o' && string[2]=='n' && string[3]=='e')
247                 i = 4;
248         /* If next source char is an identifier (eg. 'i' in "definate") no match */
249         if(i==0 || text_check_identifier(string[i]))
250                 return -1;
251         return i;
252 }
253
254 /* Ensures the format string for the given line is long enough, reallocating
255  as needed. Allocation is done here, alone, to ensure consistency. */
256 static int text_check_format_len(TextLine *line, unsigned int len)
257 {
258         if(line->format) {
259                 if(strlen(line->format) < len) {
260                         MEM_freeN(line->format);
261                         line->format = MEM_mallocN(len+2, "SyntaxFormat");
262                         if(!line->format) return 0;
263                 }
264         }
265         else {
266                 line->format = MEM_mallocN(len+2, "SyntaxFormat");
267                 if(!line->format) return 0;
268         }
269
270         return 1;
271 }
272
273 /* Formats the specified line. If do_next is set, the process will move on to
274  the succeeding line if it is affected (eg. multiline strings). Format strings
275  may contain any of the following characters:
276          '_'            Whitespace
277          '#'            Comment text
278          '!'            Punctuation and other symbols
279          'n'            Numerals
280          'l'            String letters
281          'v'            Special variables (class, def)
282          'b'            Built-in names (print, for, etc.)
283          'q'            Other text (identifiers, etc.)
284  It is terminated with a null-terminator '\0' followed by a continuation
285  flag indicating whether the line is part of a multi-line string. */
286
287 static void txt_format_line(SpaceText *st, TextLine *line, int do_next)
288 {
289         FlattenString fs;
290         char *str, *fmt, orig, cont, find, prev = ' ';
291         int len, i;
292
293         /* Get continuation from previous line */
294         if(line->prev && line->prev->format != NULL) {
295                 fmt= line->prev->format;
296                 cont = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
297         }
298         else cont = 0;
299
300         /* Get original continuation from this line */
301         if(line->format != NULL) {
302                 fmt= line->format;
303                 orig = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
304         }
305         else orig = 0xFF;
306
307         flatten_string(st, &fs, line->line);
308         str = fs.buf;
309         len = strlen(str);
310         if(!text_check_format_len(line, len)) {
311                 flatten_string_free(&fs);
312                 return;
313         }
314         fmt = line->format;
315
316         while(*str) {
317                 /* Handle escape sequences by skipping both \ and next char */
318                 if(*str == '\\') {
319                         *fmt = prev; fmt++; str++;
320                         if(*str == '\0') break;
321                         *fmt = prev; fmt++; str++;
322                         continue;
323                 }
324                 /* Handle continuations */
325                 else if(cont) {
326                         /* Triple strings ("""...""" or '''...''') */
327                         if(cont & TXT_TRISTR) {
328                                 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
329                                 if(*str==find && *(str+1)==find && *(str+2)==find) {
330                                         *fmt = 'l'; fmt++; str++;
331                                         *fmt = 'l'; fmt++; str++;
332                                         cont = 0;
333                                 }
334                         /* Handle other strings */
335                         }
336                         else {
337                                 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
338                                 if(*str == find) cont = 0;
339                         }
340
341                         *fmt = 'l';
342                 }
343                 /* Not in a string... */
344                 else {
345                         /* Deal with comments first */
346                         if(prev == '#' || *str == '#')
347                                 *fmt = '#';
348                         /* Strings */
349                         else if(*str == '"' || *str == '\'') {
350                                 find = *str;
351                                 cont = (*str== '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR;
352                                 if(*(str+1) == find && *(str+2) == find) {
353                                         *fmt = 'l'; fmt++; str++;
354                                         *fmt = 'l'; fmt++; str++;
355                                         cont |= TXT_TRISTR;
356                                 }
357                                 *fmt = 'l';
358                         }
359                         /* Whitespace (all ws. has been converted to spaces) */
360                         else if(*str == ' ')
361                                 *fmt = '_';
362                         /* Numbers (digits not part of an identifier and periods followed by digits) */
363                         else if((prev != 'q' && text_check_digit(*str)) || (*str == '.' && text_check_digit(*(str+1))))
364                                 *fmt = 'n';
365                         /* Booleans */
366                         else if(prev != 'q' && (i=find_bool(str)) != -1)
367                                 if(i>0) {
368                                         while(i>1) {
369                                                 *fmt = 'n'; fmt++; str++;
370                                                 i--;
371                                         }
372                                         *fmt = 'n';
373                                 }
374                                 else
375                                         *fmt = 'q';
376                         /* Punctuation */
377                         else if(text_check_delim(*str))
378                                 *fmt = '!';
379                         /* Identifiers and other text (no previous ws. or delims. so text continues) */
380                         else if(prev == 'q')
381                                 *fmt = 'q';
382                         /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
383                         else {
384                                 /* Special vars(v) or built-in keywords(b) */
385                                 if((i=find_specialvar(str)) != -1)
386                                         prev = 'v';
387                                 else if((i=find_builtinfunc(str)) != -1)
388                                         prev = 'b';
389                                 else if((i=find_decorator(str)) != -1)
390                                         prev = 'v'; /* could have a new color for this */
391                                 if(i>0) {
392                                         while(i>1) {
393                                                 *fmt = prev; fmt++; str++;
394                                                 i--;
395                                         }
396                                         *fmt = prev;
397                                 }
398                                 else
399                                         *fmt = 'q';
400                         }
401                 }
402                 prev = *fmt;
403                 fmt++;
404                 str++;
405         }
406
407         /* Terminate and add continuation char */
408         *fmt = '\0'; fmt++;
409         *fmt = cont;
410
411         /* Debugging */
412         //print_format(st, line);
413
414         /* If continuation has changed and we're allowed, process the next line */
415         if(cont!=orig && do_next && line->next) {
416                 txt_format_line(st, line->next, do_next);
417         }
418
419         flatten_string_free(&fs);
420 }
421
422 #if 0
423 /* Formats every line of the current text */
424 static void txt_format_text(SpaceText *st) 
425 {
426         TextLine *linep;
427
428         if(!st->text) return;
429
430         for(linep=st->text->lines.first; linep; linep=linep->next)
431                 txt_format_line(st, linep, 0);
432 }
433 #endif
434
435 /* Sets the current drawing color based on the format character specified */
436 static void format_draw_color(char formatchar)
437 {
438         switch (formatchar) {
439                 case '_': /* Whitespace */
440                         break;
441                 case '!': /* Symbols */
442                         UI_ThemeColorBlend(TH_TEXT, TH_BACK, 0.5f);
443                         break;
444                 case '#': /* Comments */
445                         UI_ThemeColor(TH_SYNTAX_C);
446                         break;
447                 case 'n': /* Numerals */
448                         UI_ThemeColor(TH_SYNTAX_N);
449                         break;
450                 case 'l': /* Strings */
451                         UI_ThemeColor(TH_SYNTAX_L);
452                         break;
453                 case 'v': /* Specials: class, def */
454                         UI_ThemeColor(TH_SYNTAX_V);
455                         break;
456                 case 'b': /* Keywords: for, print, etc. */
457                         UI_ThemeColor(TH_SYNTAX_B);
458                         break;
459                 case 'q': /* Other text (identifiers) */
460                 default:
461                         UI_ThemeColor(TH_TEXT);
462                         break;
463         }
464 }
465
466 /************************** draw text *****************************/
467
468 /***********************/ /*
469
470 Notes on word-wrap
471 --
472 All word-wrap functions follow the algorithm below to maintain consistency.
473         line            The line to wrap (tabs converted to spaces)
474         view_width      The maximum number of characters displayable in the region
475                                 This equals region_width/font_width for the region
476         wrap_chars      Characters that allow wrapping. This equals [' ', '\t', '-']
477
478 def wrap(line, view_width, wrap_chars):
479         draw_start = 0
480         draw_end = view_width
481         pos = 0
482         for c in line:
483                 if pos-draw_start >= view_width:
484                         print line[draw_start:draw_end]
485                         draw_start = draw_end
486                         draw_end += view_width
487                 elif c in wrap_chars:
488                         draw_end = pos+1
489                 pos += 1
490         print line[draw_start:]
491
492 */ /***********************/
493
494 int wrap_width(SpaceText *st, ARegion *ar)
495 {
496         int winx= ar->winx - TXT_SCROLL_WIDTH;
497         int x, max;
498         
499         x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
500         max= st->cwidth ? (winx-x)/st->cwidth : 0;
501         return max>8 ? max : 8;
502 }
503
504 /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
505 void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
506 {
507         Text *text;
508         TextLine *linep;
509         int i, j, start, end, max, chop;
510         char ch;
511
512         *offl= *offc= 0;
513
514         if(!st->text) return;
515         if(!st->wordwrap) return;
516
517         text= st->text;
518
519         /* Move pointer to first visible line (top) */
520         linep= text->lines.first;
521         i= st->top;
522         while(i>0 && linep) {
523                 int lines= text_get_visible_lines(st, ar, linep->line);
524
525                 /* Line before top */
526                 if(linep == linein) {
527                         if(lines <= i)
528                                 /* no visible part of line */
529                                 return;
530                 }
531
532                 if (i-lines<0) {
533                         break;
534                 } else {
535                         linep= linep->next;
536                         (*offl)+= lines-1;
537                         i-= lines;
538                 }
539         }
540
541         max= wrap_width(st, ar);
542
543         while(linep) {
544                 start= 0;
545                 end= max;
546                 chop= 1;
547                 *offc= 0;
548                 for(i=0, j=0; linep->line[j]!='\0'; j++) {
549                         int chars;
550
551                         /* Mimic replacement of tabs */
552                         ch= linep->line[j];
553                         if(ch=='\t') {
554                                 chars= st->tabnumber-i%st->tabnumber;
555                                 if(linep==linein && i<cursin) cursin += chars-1;
556                                 ch= ' ';
557                         }
558                         else {
559                                 chars= 1;
560                         }
561
562                         while(chars--) {
563                                 if(i-start>=max) {
564                                         if(chop && linep==linein && i >= cursin) {
565                                                 if (i==cursin) {
566                                                         (*offl)++;
567                                                         *offc -= end-start;
568                                                 }
569
570                                                 return;
571                                         }
572
573                                         (*offl)++;
574                                         *offc -= end-start;
575
576                                         start= end;
577                                         end += max;
578                                         chop= 1;
579                                 }
580                                 else if(ch==' ' || ch=='-') {
581                                         end = i+1;
582                                         chop= 0;
583                                         if(linep==linein && i >= cursin)
584                                                 return;
585                                 }
586                                 i++;
587                         }
588                 }
589                 if(linep==linein) break;
590                 linep= linep->next;
591         }
592 }
593
594 void wrap_offset_in_line(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
595 {
596         int i, j, start, end, chars, max, chop;
597         char ch;
598
599         *offl= *offc= 0;
600
601         if(!st->text) return;
602         if(!st->wordwrap) return;
603
604         max= wrap_width(st, ar);
605
606         start= 0;
607         end= max;
608         chop= 1;
609         *offc= 0;
610
611         for(i=0, j=0; linein->line[j]!='\0'; j++) {
612
613                 /* Mimic replacement of tabs */
614                 ch= linein->line[j];
615                 if(ch=='\t') {
616                         chars= st->tabnumber-i%st->tabnumber;
617                         if(i<cursin) cursin += chars-1;
618                         ch= ' ';
619                 }
620                 else
621                         chars= 1;
622
623                 while(chars--) {
624                         if(i-start>=max) {
625                                 if(chop && i >= cursin) {
626                                         if (i==cursin) {
627                                                 (*offl)++;
628                                                 *offc -= end-start;
629                                         }
630
631                                         return;
632                                 }
633
634                                 (*offl)++;
635                                 *offc -= end-start;
636
637                                 start= end;
638                                 end += max;
639                                 chop= 1;
640                         }
641                         else if(ch==' ' || ch=='-') {
642                                 end = i+1;
643                                 chop= 0;
644                                 if(i >= cursin)
645                                         return;
646                         }
647                         i++;
648                 }
649         }
650 }
651
652 int text_get_char_pos(SpaceText *st, const char *line, int cur)
653 {
654         int a=0, i;
655         
656         for(i=0; i<cur && line[i]; i++) {
657                 if(line[i]=='\t')
658                         a += st->tabnumber-a%st->tabnumber;
659                 else
660                         a++;
661         }
662         return a;
663 }
664
665 static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format, int skip)
666 {
667         FlattenString fs;
668         int basex, i, a, len, start, end, max, lines;
669         
670         len= flatten_string(st, &fs, str);
671         str= fs.buf;
672         max= w/st->cwidth;
673         if(max<8) max= 8;
674         basex= x;
675
676         lines= 1;
677         start= 0;
678         end= max;
679         for(i=0; i<len; i++) {
680                 if(i-start >= max) {
681                         /* skip hidden part of line */
682                         if(skip) {
683                                 skip--;
684                                 start= end;
685                                 end += max;
686                                 continue;
687                         }
688
689                         /* Draw the visible portion of text on the overshot line */
690                         for(a=start; a<end; a++) {
691                                 if(st->showsyntax && format) format_draw_color(format[a]);
692                                 x += text_font_draw_character(st, x, y, str[a]);
693                         }
694                         y -= st->lheight;
695                         x= basex;
696                         lines++;
697                         start= end;
698                         end += max;
699
700                         if(y<=0) break;
701                 }
702                 else if(str[i]==' ' || str[i]=='-') {
703                         end = i+1;
704                 }
705         }
706
707         /* Draw the remaining text */
708         for(a=start; a<len && y > 0; a++) {
709                 if(st->showsyntax && format)
710                         format_draw_color(format[a]);
711
712                 x += text_font_draw_character(st, x, y, str[a]);
713         }
714
715         flatten_string_free(&fs);
716
717         return lines;
718 }
719
720 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format)
721 {
722         FlattenString fs;
723         int r=0, w= 0, amount;
724         int *acc;
725         char *in;
726
727         w= flatten_string(st, &fs, str);
728         if(w < cshift) {
729                 flatten_string_free(&fs);
730                 return 0; /* String is shorter than shift */
731         }
732         
733         in= fs.buf+cshift;
734         acc= fs.accum+cshift;
735         w= w-cshift;
736
737         if(draw) {
738                 if(st->showsyntax && format) {
739                         int a;
740                         format = format+cshift;
741                 
742                         amount = strlen(in);
743                         if(maxwidth)
744                                 amount= MIN2(amount, maxwidth);
745                         
746                         for(a = 0; a < amount; a++) {
747                                 format_draw_color(format[a]);
748                                 x += text_font_draw_character(st, x, y, in[a]);
749                         }
750                 }
751                 else {
752                         amount = strlen(in);
753                         if(maxwidth)
754                                 amount= MIN2(amount, maxwidth);
755
756                         in[amount]= 0;
757                         text_font_draw(st, x, y, in);
758                 }
759         }
760         else {
761                 while(w-- && *acc++ < maxwidth)
762                         r+= st->cwidth;
763         }
764
765         flatten_string_free(&fs);
766
767         if(cshift && r==0)
768                 return 0;
769         else if(st->showlinenrs)
770                 return r+TXT_OFFSET+TEXTXLOC;
771         else
772                 return r+TXT_OFFSET;
773 }
774
775 /************************ cache utilities *****************************/
776
777 typedef struct DrawCache {
778         int *line_height;
779         int total_lines, nlines;
780
781         /* this is needed to check cache relevance */
782         int winx, wordwrap, showlinenrs, tabnumber;
783         short lheight;
784         char cwidth;
785         char text_id[MAX_ID_NAME];
786
787         /* for partial lines recalculation */
788         short update_flag;
789         int valid_head, valid_tail; /* amount of unchanged lines */
790 } DrawCache;
791
792 static void text_drawcache_init(SpaceText *st)
793 {
794         DrawCache *drawcache= MEM_callocN(sizeof (DrawCache), "text draw cache");
795
796         drawcache->winx= -1;
797         drawcache->nlines= BLI_countlist(&st->text->lines);
798         drawcache->text_id[0]= '\0';
799
800         st->drawcache= drawcache;
801 }
802
803 static void text_update_drawcache(SpaceText *st, ARegion *ar)
804 {
805         DrawCache *drawcache;
806         int full_update= 0, nlines= 0;
807         Text *txt= st->text;
808
809         if(!st->drawcache) text_drawcache_init(st);
810
811         text_update_character_width(st);
812
813         drawcache= (DrawCache *)st->drawcache;
814         nlines= drawcache->nlines;
815
816         /* check if full cache update is needed */
817         full_update|= drawcache->winx != ar->winx;                 /* area was resized */
818         full_update|= drawcache->wordwrap != st->wordwrap;         /* word-wrapping option was toggled */
819         full_update|= drawcache->showlinenrs != st->showlinenrs; /* word-wrapping option was toggled */
820         full_update|= drawcache->tabnumber != st->tabnumber;  /* word-wrapping option was toggled */
821         full_update|= drawcache->lheight != st->lheight;      /* word-wrapping option was toggled */
822         full_update|= drawcache->cwidth != st->cwidth;        /* word-wrapping option was toggled */
823         full_update|= strncmp(drawcache->text_id, txt->id.name, MAX_ID_NAME); /* text datablock was changed */
824
825         if(st->wordwrap) {
826                 /* update line heights */
827                 if(full_update || !drawcache->line_height) {
828                         drawcache->valid_head  = 0;
829                         drawcache->valid_tail  = 0;
830                         drawcache->update_flag = 1;
831                 }
832
833                 if(drawcache->update_flag) {
834                         TextLine *line= st->text->lines.first;
835                         int lineno= 0, size, lines_count;
836                         int *fp= drawcache->line_height, *new_tail, *old_tail;
837
838                         nlines= BLI_countlist(&txt->lines);
839                         size= sizeof(int)*nlines;
840
841                         if(fp) fp= MEM_reallocN(fp, size);
842                         else fp= MEM_callocN(size, "text drawcache line_height");
843
844                         drawcache->valid_tail= drawcache->valid_head= 0;
845                         old_tail= fp + drawcache->nlines - drawcache->valid_tail;
846                         new_tail= fp + nlines - drawcache->valid_tail;
847                         memmove(new_tail, old_tail, drawcache->valid_tail);
848
849                         drawcache->total_lines= 0;
850
851                         if(st->showlinenrs)
852                                 st->linenrs_tot= (int)floor(log10((float)nlines)) + 1;
853
854                         while(line) {
855                                 if(drawcache->valid_head) { /* we're inside valid head lines */
856                                         lines_count= fp[lineno];
857                                         drawcache->valid_head--;
858                                 } else if (lineno > new_tail - fp) {  /* we-re inside valid tail lines */
859                                         lines_count= fp[lineno];
860                                 } else {
861                                         lines_count= text_get_visible_lines(st, ar, line->line);
862                                 }
863
864                                 fp[lineno]= lines_count;
865
866                                 line= line->next;
867                                 lineno++;
868                                 drawcache->total_lines+= lines_count;
869                         }
870
871                         drawcache->line_height= fp;
872                 }
873         } else {
874                 if(drawcache->line_height) {
875                         MEM_freeN(drawcache->line_height);
876                         drawcache->line_height= NULL;
877                 }
878
879                 if(full_update || drawcache->update_flag) {
880                         nlines= BLI_countlist(&txt->lines);
881
882                         if(st->showlinenrs)
883                                 st->linenrs_tot= (int)floor(log10((float)nlines)) + 1;
884                 }
885
886                 drawcache->total_lines= nlines;
887         }
888
889         drawcache->nlines= nlines;
890
891         /* store settings */
892         drawcache->winx        = ar->winx;
893         drawcache->wordwrap    = st->wordwrap;
894         drawcache->lheight     = st->lheight;
895         drawcache->cwidth      = st->cwidth;
896         drawcache->showlinenrs = st->showlinenrs;
897         drawcache->tabnumber   = st->tabnumber;
898
899         strncpy(drawcache->text_id, txt->id.name, MAX_ID_NAME);
900
901         /* clear update flag */
902         drawcache->update_flag = 0;
903         drawcache->valid_head  = 0;
904         drawcache->valid_tail  = 0;
905 }
906
907 void text_drawcache_tag_update(SpaceText *st, int full)
908 {
909         /* this happens if text editor ops are caled from python */
910         if (st == NULL)
911                 return;
912                 
913         if(st->drawcache) {
914                 DrawCache *drawcache= (DrawCache *)st->drawcache;
915                 Text *txt= st->text;
916
917                 if(drawcache->update_flag) {
918                         /* happens when tagging update from space listener */
919                         /* should do nothing to prevent locally tagged cache be fully recalculated */
920                         return;
921                 }
922
923                 if(!full) {
924                         int sellno= BLI_findindex(&txt->lines, txt->sell);
925                         int curlno= BLI_findindex(&txt->lines, txt->curl);
926
927                         if(curlno < sellno) {
928                                 drawcache->valid_head= curlno;
929                                 drawcache->valid_tail= drawcache->nlines - sellno - 1;
930                         } else {
931                                 drawcache->valid_head= sellno;
932                                 drawcache->valid_tail= drawcache->nlines - curlno - 1;
933                         }
934
935                         /* quick cache recalculation is also used in delete operator,
936                            which could merge lines which are adjusent to current selection lines
937                            expand recalculate area to this lines */
938                         if(drawcache->valid_head>0) drawcache->valid_head--;
939                         if(drawcache->valid_tail>0) drawcache->valid_tail--;
940                 } else {
941                         drawcache->valid_head= 0;
942                         drawcache->valid_tail= 0;
943                 }
944
945                 drawcache->update_flag= 1;
946         }
947 }
948
949 void text_free_caches(SpaceText *st)
950 {
951         DrawCache *drawcache= (DrawCache *)st->drawcache;
952
953         if(drawcache) {
954                 if(drawcache->line_height)
955                         MEM_freeN(drawcache->line_height);
956
957                 MEM_freeN(drawcache);
958         }
959 }
960
961 /************************ word-wrap utilities *****************************/
962
963 /* cache should be updated in caller */
964 static int text_get_visible_lines_no(SpaceText *st, int lineno)
965 {
966         DrawCache *drawcache= (DrawCache *)st->drawcache;
967
968         return drawcache->line_height[lineno];
969 }
970
971 int text_get_visible_lines(SpaceText *st, ARegion *ar, const char *str)
972 {
973         int i, j, start, end, max, lines, chars;
974         char ch;
975
976         max= wrap_width(st, ar);
977         lines= 1;
978         start= 0;
979         end= max;
980         for(i= 0, j= 0; str[j] != '\0'; j++) {
981                 /* Mimic replacement of tabs */
982                 ch= str[j];
983                 if(ch=='\t') {
984                         chars= st->tabnumber-i%st->tabnumber;
985                         ch= ' ';
986                 }
987                 else chars= 1;
988
989                 while(chars--) {
990                         if(i-start >= max) {
991                                 lines++;
992                                 start= end;
993                                 end += max;
994                         }
995                         else if(ch==' ' || ch=='-') {
996                                 end= i+1;
997                         }
998
999                         i++;
1000                 }
1001         }
1002
1003         return lines;
1004 }
1005
1006 int text_get_span_wrap(SpaceText *st, ARegion *ar, TextLine *from, TextLine *to)
1007 {
1008         if(st->wordwrap) {
1009                 int ret=0;
1010                 TextLine *tmp= from;
1011
1012                 /* Look forwards */
1013                 while (tmp) {
1014                         if (tmp == to) return ret;
1015                         ret+= text_get_visible_lines(st, ar, tmp->line);
1016                         tmp= tmp->next;
1017                 }
1018
1019                 return ret;
1020         } else return txt_get_span(from, to);
1021 }
1022
1023 int text_get_total_lines(SpaceText *st, ARegion *ar)
1024 {
1025         DrawCache *drawcache;
1026
1027         text_update_drawcache(st, ar);
1028         drawcache= (DrawCache *)st->drawcache;
1029
1030         return drawcache->total_lines;
1031 }
1032
1033 /* Move pointer to first visible line (top) */
1034 static TextLine *first_visible_line(SpaceText *st, ARegion *ar, int *wrap_top)
1035 {
1036         Text *text= st->text;
1037         TextLine* pline= text->lines.first;
1038         int i= st->top, lineno= 0;
1039
1040         text_update_drawcache(st, ar);
1041
1042         if(wrap_top) *wrap_top= 0;
1043
1044         if(st->wordwrap) {
1045                 while(i>0 && pline) {
1046                         int lines= text_get_visible_lines_no(st, lineno);
1047
1048                         if (i-lines<0) {
1049                                 if(wrap_top) *wrap_top= i;
1050                                 break;
1051                         } else {
1052                                 pline= pline->next;
1053                                 i-= lines;
1054                                 lineno++;
1055                         }
1056                 }
1057         } else {
1058                 for(i=st->top; pline->next && i>0; i--)
1059                         pline= pline->next;
1060         }
1061
1062         return pline;
1063 }
1064
1065 /************************ draw scrollbar *****************************/
1066
1067 static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll, rcti *back)
1068 {
1069         int lhlstart, lhlend, ltexth, sell_off, curl_off;
1070         short barheight, barstart, hlstart, hlend, blank_lines;
1071         short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
1072
1073         pix_top_margin = 8;
1074         pix_bottom_margin = 4;
1075         pix_available = ar->winy - pix_top_margin - pix_bottom_margin;
1076         ltexth= text_get_total_lines(st, ar);
1077         blank_lines = st->viewlines / 2;
1078         
1079         /* nicer code: use scroll rect for entire bar */
1080         back->xmin= ar->winx -18;
1081         back->xmax= ar->winx;
1082         back->ymin= 0;
1083         back->ymax= ar->winy;
1084         
1085         scroll->xmin= ar->winx - 17;
1086         scroll->xmax= ar->winx - 5;
1087         scroll->ymin= 4;
1088         scroll->ymax= 4+pix_available;
1089         
1090         /* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */
1091         if(ltexth + blank_lines < st->top + st->viewlines) {
1092                 blank_lines = st->top + st->viewlines - ltexth;
1093         }
1094         
1095         ltexth += blank_lines;
1096
1097         barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0;
1098         pix_bardiff = 0;
1099         if(barheight < 20) {
1100                 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */      
1101                 barheight = 20;
1102         }
1103         barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0;
1104
1105         st->txtbar= *scroll;
1106         st->txtbar.ymax -= barstart;
1107         st->txtbar.ymin = st->txtbar.ymax - barheight;
1108
1109         CLAMP(st->txtbar.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
1110         CLAMP(st->txtbar.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
1111
1112         st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
1113         if(st->pix_per_line < 0.1f) st->pix_per_line=0.1f;
1114
1115         curl_off= text_get_span_wrap(st, ar, st->text->lines.first, st->text->curl);
1116         sell_off= text_get_span_wrap(st, ar, st->text->lines.first, st->text->sell);
1117         lhlstart = MIN2(curl_off, sell_off);
1118         lhlend = MAX2(curl_off, sell_off);
1119
1120         if(ltexth > 0) {
1121                 hlstart = (lhlstart * pix_available)/ltexth;
1122                 hlend = (lhlend * pix_available)/ltexth;
1123
1124                 /* the scrollbar is non-linear sized */
1125                 if(pix_bardiff > 0) {
1126                         /* the start of the highlight is in the current viewport */
1127                         if(ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) { 
1128                                 /* speed the progresion of the start of the highlight through the scrollbar */
1129                                 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);       
1130                         }
1131                         else if(lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
1132                                 /* push hl start down */
1133                                 hlstart = barstart + barheight;
1134                         }
1135                         else if(lhlend > st->top  && lhlstart < st->top && hlstart > barstart) {
1136                                 /*fill out start */
1137                                 hlstart = barstart;
1138                         }
1139
1140                         if(hlend <= hlstart) { 
1141                                 hlend = hlstart + 2;
1142                         }
1143
1144                         /* the end of the highlight is in the current viewport */
1145                         if(ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) { 
1146                                 /* speed the progresion of the end of the highlight through the scrollbar */
1147                                 hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines);    
1148                         }
1149                         else if(lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
1150                                 /* push hl end up */
1151                                 hlend = barstart;
1152                         }                                       
1153                         else if(lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
1154                                 /* fill out end */
1155                                 hlend = barstart + barheight;
1156                         }
1157
1158                         if(hlend <= hlstart) { 
1159                                 hlstart = hlend - 2;
1160                         }       
1161                 }       
1162         }
1163         else {
1164                 hlstart = 0;
1165                 hlend = 0;
1166         }
1167
1168         if(hlend - hlstart < 2) { 
1169                 hlend = hlstart + 2;
1170         }
1171         
1172         st->txtscroll= *scroll;
1173         st->txtscroll.ymax= ar->winy - pix_top_margin - hlstart;
1174         st->txtscroll.ymin= ar->winy - pix_top_margin - hlend;
1175
1176         CLAMP(st->txtscroll.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
1177         CLAMP(st->txtscroll.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
1178 }
1179
1180 static void draw_textscroll(SpaceText *st, rcti *scroll, rcti *back)
1181 {
1182         bTheme *btheme= UI_GetTheme();
1183         uiWidgetColors wcol= btheme->tui.wcol_scroll;
1184         unsigned char col[4];
1185         float rad;
1186         
1187         UI_ThemeColor(TH_BACK);
1188         glRecti(back->xmin, back->ymin, back->xmax, back->ymax);
1189
1190         uiWidgetScrollDraw(&wcol, scroll, &st->txtbar, (st->flags & ST_SCROLL_SELECT)?UI_SCROLL_PRESSED:0);
1191
1192         uiSetRoundBox(UI_CNR_ALL);
1193         rad= 0.4f*MIN2(st->txtscroll.xmax - st->txtscroll.xmin, st->txtscroll.ymax - st->txtscroll.ymin);
1194         UI_GetThemeColor3ubv(TH_HILITE, col);
1195         col[3]= 48;
1196         glColor4ubv(col);
1197         glEnable(GL_BLEND);
1198         uiRoundBox(st->txtscroll.xmin+1, st->txtscroll.ymin, st->txtscroll.xmax-1, st->txtscroll.ymax, rad);
1199         glDisable(GL_BLEND);
1200 }
1201
1202 /************************** draw markers **************************/
1203
1204 static void draw_markers(SpaceText *st, ARegion *ar)
1205 {
1206         Text *text= st->text;
1207         TextMarker *marker, *next;
1208         TextLine *top, *line;
1209         int offl, offc, i, x1, x2, y1, y2, x, y;
1210         int topi, topy;
1211
1212         /* Move pointer to first visible line (top) */
1213         top= first_visible_line(st, ar, NULL);
1214         topi= BLI_findindex(&text->lines, top);
1215
1216         topy= txt_get_span(text->lines.first, top);
1217
1218         for(marker= text->markers.first; marker; marker= next) {
1219                 next= marker->next;
1220
1221                 /* invisible line (before top) */
1222                 if(marker->lineno<topi) continue;
1223
1224                 line= BLI_findlink(&text->lines, marker->lineno);
1225
1226                 /* Remove broken markers */
1227                 if(marker->end>line->len || marker->start>marker->end) {
1228                         BLI_freelinkN(&text->markers, marker);
1229                         continue;
1230                 }
1231
1232                 wrap_offset(st, ar, line, marker->start, &offl, &offc);
1233                 y1 = txt_get_span(top, line) - st->top + offl + topy;
1234                 x1 = text_get_char_pos(st, line->line, marker->start) - st->left + offc;
1235
1236                 wrap_offset(st, ar, line, marker->end, &offl, &offc);
1237                 y2 = txt_get_span(top, line) - st->top + offl + topy;
1238                 x2 = text_get_char_pos(st, line->line, marker->end) - st->left + offc;
1239
1240                 /* invisible part of line (before top, after last visible line) */
1241                 if(y2 < 0 || y1 > st->top+st->viewlines) continue;
1242
1243                 glColor3ubv(marker->color);
1244                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1245                 y= ar->winy-3;
1246
1247                 if(y1==y2) {
1248                         y -= y1*st->lheight;
1249                         glBegin(GL_LINE_LOOP);
1250                         glVertex2i(x+x2*st->cwidth+1, y);
1251                         glVertex2i(x+x1*st->cwidth-2, y);
1252                         glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
1253                         glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
1254                         glEnd();
1255                 }
1256                 else {
1257                         y -= y1*st->lheight;
1258                         glBegin(GL_LINE_STRIP);
1259                         glVertex2i(ar->winx, y);
1260                         glVertex2i(x+x1*st->cwidth-2, y);
1261                         glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
1262                         glVertex2i(ar->winx, y-st->lheight);
1263                         glEnd();
1264                         y-=st->lheight;
1265
1266                         for(i=y1+1; i<y2; i++) {
1267                                 glBegin(GL_LINES);
1268                                 glVertex2i(x, y);
1269                                 glVertex2i(ar->winx, y);
1270                                 glVertex2i(x, y-st->lheight);
1271                                 glVertex2i(ar->winx, y-st->lheight);
1272                                 glEnd();
1273                                 y-=st->lheight;
1274                         }
1275
1276                         glBegin(GL_LINE_STRIP);
1277                         glVertex2i(x, y);
1278                         glVertex2i(x+x2*st->cwidth+1, y);
1279                         glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
1280                         glVertex2i(x, y-st->lheight);
1281                         glEnd();
1282                 }
1283         }
1284 }
1285
1286 /*********************** draw documentation *******************************/
1287
1288 static void draw_documentation(SpaceText *st, ARegion *ar)
1289 {
1290         TextLine *tmp;
1291         char *docs, buf[DOC_WIDTH+1], *p;
1292         int i, br, lines;
1293         int boxw, boxh, l, x, y /* , top */ /* UNUSED */;
1294         
1295         if(!st || !st->text) return;
1296         if(!texttool_text_is_active(st->text)) return;
1297         
1298         docs = texttool_docs_get();
1299
1300         if(!docs) return;
1301
1302         /* Count the visible lines to the cursor */
1303         for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1304         if(l<0) return;
1305         
1306         if(st->showlinenrs) {
1307                 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1308         }
1309         else {
1310                 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4;
1311         }
1312         if(texttool_suggest_first()) {
1313                 x += SUGG_LIST_WIDTH*st->cwidth + 50;
1314         }
1315
1316         /* top= */ /* UNUSED */ y= ar->winy - st->lheight*l - 2;
1317         boxw= DOC_WIDTH*st->cwidth + 20;
1318         boxh= (DOC_HEIGHT+1)*st->lheight;
1319
1320         /* Draw panel */
1321         UI_ThemeColor(TH_BACK);
1322         glRecti(x, y, x+boxw, y-boxh);
1323         UI_ThemeColor(TH_SHADE1);
1324         glBegin(GL_LINE_LOOP);
1325         glVertex2i(x, y);
1326         glVertex2i(x+boxw, y);
1327         glVertex2i(x+boxw, y-boxh);
1328         glVertex2i(x, y-boxh);
1329         glEnd();
1330         glBegin(GL_LINE_LOOP);
1331         glVertex2i(x+boxw-10, y-7);
1332         glVertex2i(x+boxw-4, y-7);
1333         glVertex2i(x+boxw-7, y-2);
1334         glEnd();
1335         glBegin(GL_LINE_LOOP);
1336         glVertex2i(x+boxw-10, y-boxh+7);
1337         glVertex2i(x+boxw-4, y-boxh+7);
1338         glVertex2i(x+boxw-7, y-boxh+2);
1339         glEnd();
1340         UI_ThemeColor(TH_TEXT);
1341
1342         i= 0; br= DOC_WIDTH; lines= 0; // XXX -doc_scroll;
1343         for(p=docs; *p; p++) {
1344                 if(*p == '\r' && *(++p) != '\n') *(--p)= '\n'; /* Fix line endings */
1345                 if(*p == ' ' || *p == '\t')
1346                         br= i;
1347                 else if(*p == '\n') {
1348                         buf[i]= '\0';
1349                         if(lines>=0) {
1350                                 y -= st->lheight;
1351                                 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1352                         }
1353                         i= 0; br= DOC_WIDTH; lines++;
1354                 }
1355                 buf[i++]= *p;
1356                 if(i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
1357                         buf[br]= '\0';
1358                         if(lines>=0) {
1359                                 y -= st->lheight;
1360                                 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1361                         }
1362                         p -= i-br-1; /* Rewind pointer to last break */
1363                         i= 0; br= DOC_WIDTH; lines++;
1364                 }
1365                 if(lines >= DOC_HEIGHT) break;
1366         }
1367
1368         if(0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) {
1369                 // XXX doc_scroll--;
1370                 draw_documentation(st, ar);
1371         }
1372 }
1373
1374 /*********************** draw suggestion list *******************************/
1375
1376 static void draw_suggestion_list(SpaceText *st, ARegion *ar)
1377 {
1378         SuggItem *item, *first, *last, *sel;
1379         TextLine *tmp;
1380         char str[SUGG_LIST_WIDTH+1];
1381         int w, boxw=0, boxh, i, l, x, y, b, *top;
1382         
1383         if(!st || !st->text) return;
1384         if(!texttool_text_is_active(st->text)) return;
1385
1386         first = texttool_suggest_first();
1387         last = texttool_suggest_last();
1388
1389         if(!first || !last) return;
1390
1391         text_pop_suggest_list();
1392         sel = texttool_suggest_selected();
1393         top = texttool_suggest_top();
1394
1395         /* Count the visible lines to the cursor */
1396         for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1397         if(l<0) return;
1398         
1399         if(st->showlinenrs) {
1400                 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1401         }
1402         else {
1403                 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4;
1404         }
1405         y = ar->winy - st->lheight*l - 2;
1406
1407         boxw = SUGG_LIST_WIDTH*st->cwidth + 20;
1408         boxh = SUGG_LIST_SIZE*st->lheight + 8;
1409         
1410         UI_ThemeColor(TH_SHADE1);
1411         glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
1412         UI_ThemeColor(TH_BACK);
1413         glRecti(x, y, x+boxw, y-boxh);
1414
1415         /* Set the top 'item' of the visible list */
1416         for(i=0, item=first; i<*top && item->next; i++, item=item->next);
1417
1418         for(i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) {
1419
1420                 y -= st->lheight;
1421
1422                 BLI_strncpy(str, item->name, SUGG_LIST_WIDTH);
1423
1424                 w = text_font_width(st, str);
1425                 
1426                 if(item == sel) {
1427                         UI_ThemeColor(TH_SHADE2);
1428                         glRecti(x+16, y-3, x+16+w, y+st->lheight-3);
1429                 }
1430                 b=1; /* b=1 color block, text is default. b=0 no block, color text */
1431                 switch (item->type) {
1432                         case 'k': UI_ThemeColor(TH_SYNTAX_B); b=0; break;
1433                         case 'm': UI_ThemeColor(TH_TEXT); break;
1434                         case 'f': UI_ThemeColor(TH_SYNTAX_L); break;
1435                         case 'v': UI_ThemeColor(TH_SYNTAX_N); break;
1436                         case '?': UI_ThemeColor(TH_TEXT); b=0; break;
1437                 }
1438                 if(b) {
1439                         glRecti(x+8, y+2, x+11, y+5);
1440                         UI_ThemeColor(TH_TEXT);
1441                 }
1442                 text_draw(st, str, 0, 0, 1, x+16, y-1, NULL);
1443
1444                 if(item == last) break;
1445         }
1446 }
1447
1448 /*********************** draw cursor ************************/
1449
1450 static void draw_cursor(SpaceText *st, ARegion *ar)
1451 {
1452         Text *text= st->text;
1453         int vcurl, vcurc, vsell, vselc, hidden=0;
1454         int x, y, w, i;
1455
1456         /* Draw the selection */
1457         if(text->curl!=text->sell || text->curc!=text->selc) {
1458                 int offl, offc;
1459                 /* Convert all to view space character coordinates */
1460                 wrap_offset(st, ar, text->curl, text->curc, &offl, &offc);
1461                 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
1462                 vcurc = text_get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
1463                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1464                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1465                 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1466
1467                 if(vcurc<0) vcurc=0;
1468                 if(vselc<0) vselc=0, hidden=1;
1469                 
1470                 UI_ThemeColor(TH_SHADE2);
1471                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1472                 y= ar->winy-2;
1473
1474                 if(vcurl==vsell) {
1475                         y -= vcurl*st->lheight;
1476                         if(vcurc < vselc)
1477                                 glRecti(x+vcurc*st->cwidth-1, y, x+vselc*st->cwidth, y-st->lheight);
1478                         else
1479                                 glRecti(x+vselc*st->cwidth-1, y, x+vcurc*st->cwidth, y-st->lheight);
1480                 }
1481                 else {
1482                         int froml, fromc, tol, toc;
1483
1484                         if(vcurl < vsell) {
1485                                 froml= vcurl; tol= vsell;
1486                                 fromc= vcurc; toc= vselc;
1487                         }
1488                         else {
1489                                 froml= vsell; tol= vcurl;
1490                                 fromc= vselc; toc= vcurc;
1491                         }
1492
1493                         y -= froml*st->lheight;
1494                         glRecti(x+fromc*st->cwidth-1, y, ar->winx, y-st->lheight); y-=st->lheight;
1495                         for(i=froml+1; i<tol; i++)
1496                                 glRecti(x-4, y, ar->winx, y-st->lheight),  y-=st->lheight;
1497
1498                         glRecti(x-4, y, x+toc*st->cwidth, y-st->lheight);  y-=st->lheight;
1499
1500                         (void)y;
1501                 }
1502         }
1503         else {
1504                 int offl, offc;
1505                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1506                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1507                 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1508
1509                 if(vselc<0) {
1510                         vselc= 0;
1511                         hidden= 1;
1512                 }
1513         }
1514
1515         if(st->line_hlight) {
1516                 int x1, x2, y1, y2;
1517
1518                 if(st->wordwrap) {
1519                         int visible_lines = text_get_visible_lines(st, ar, text->sell->line);
1520                         int offl, offc;
1521
1522                         wrap_offset_in_line(st, ar, text->sell, text->selc, &offl, &offc);
1523
1524                         y1= ar->winy-2 - (vsell-offl)*st->lheight;
1525                         y2= y1-st->lheight*visible_lines+1;
1526                 } else {
1527                         y1= ar->winy-2 - vsell*st->lheight;
1528                         y2= y1-st->lheight+1;
1529                 }
1530
1531                 if(!(y1<0 || y2 > ar->winy)) { /* check we need to draw */
1532                         x1= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1533                         x2= x1 + ar->winx;
1534
1535                         glColor4ub(255, 255, 255, 32);
1536                         
1537                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1538                         glEnable(GL_BLEND);
1539                         glRecti(x1-4, y1, x2, y2);
1540                         glDisable(GL_BLEND);
1541                 }
1542         }
1543         
1544         if(!hidden) {
1545                 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
1546                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1547                 x += vselc*st->cwidth;
1548                 y= ar->winy-2 - vsell*st->lheight;
1549                 
1550                 if(st->overwrite) {
1551                         char ch= text->sell->line[text->selc];
1552                         
1553                         w= st->cwidth;
1554                         if(ch=='\t')  w*= st->tabnumber-(vselc+st->left)%st->tabnumber;
1555                         
1556                         UI_ThemeColor(TH_HILITE);
1557                         glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1);
1558                 }
1559                 else {
1560                         UI_ThemeColor(TH_HILITE);
1561                         glRecti(x-1, y, x+1, y-st->lheight);
1562                 }
1563         }
1564 }
1565
1566 /******************* draw matching brackets *********************/
1567
1568 static void draw_brackets(SpaceText *st, ARegion *ar)
1569 {
1570         TextLine *startl, *endl, *linep;
1571         Text *text = st->text;
1572         int b, c, startc, endc, find, stack;
1573         int viewc, viewl, offl, offc, x, y;
1574         char ch;
1575
1576         // showsyntax must be on or else the format string will be null
1577         if(!text->curl || !st->showsyntax) return;
1578
1579         startl= text->curl;
1580         startc= text->curc;
1581         b= text_check_bracket(startl->line[startc]);
1582         if(b==0 && startc>0) b = text_check_bracket(startl->line[--startc]);
1583         if(b==0) return;
1584         
1585         linep= startl;
1586         c= startc;
1587         endl= NULL;
1588         endc= -1;
1589         find= -b;
1590         stack= 0;
1591         
1592         /* Dont highlight backets if syntax HL is off or bracket in string or comment. */
1593         if(!linep->format || linep->format[c] == 'l' || linep->format[c] == '#')
1594                 return;
1595
1596         if(b>0) {
1597                 /* opening bracket, search forward for close */
1598                 c++;
1599                 while(linep) {
1600                         while(c<linep->len) {
1601                                 if(linep->format && linep->format[c] != 'l' && linep->format[c] != '#') {
1602                                         b= text_check_bracket(linep->line[c]);
1603                                         if(b==find) {
1604                                                 if(stack==0) {
1605                                                         endl= linep;
1606                                                         endc= c;
1607                                                         break;
1608                                                 }
1609                                                 stack--;
1610                                         }
1611                                         else if(b==-find) {
1612                                                 stack++;
1613                                         }
1614                                 }
1615                                 c++;
1616                         }
1617                         if(endl) break;
1618                         linep= linep->next;
1619                         c= 0;
1620                 }
1621         }
1622         else {
1623                 /* closing bracket, search backward for open */
1624                 c--;
1625                 while(linep) {
1626                         while(c>=0) {
1627                                 if(linep->format && linep->format[c] != 'l' && linep->format[c] != '#') {
1628                                         b= text_check_bracket(linep->line[c]);
1629                                         if(b==find) {
1630                                                 if(stack==0) {
1631                                                         endl= linep;
1632                                                         endc= c;
1633                                                         break;
1634                                                 }
1635                                                 stack--;
1636                                         }
1637                                         else if(b==-find) {
1638                                                 stack++;
1639                                         }
1640                                 }
1641                                 c--;
1642                         }
1643                         if(endl) break;
1644                         linep= linep->prev;
1645                         if(linep) c= linep->len-1;
1646                 }
1647         }
1648
1649         if(!endl || endc==-1)
1650                 return;
1651
1652         UI_ThemeColor(TH_HILITE);       
1653         x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1654         y= ar->winy - st->lheight;
1655
1656         /* draw opening bracket */
1657         ch= startl->line[startc];
1658         wrap_offset(st, ar, startl, startc, &offl, &offc);
1659         viewc= text_get_char_pos(st, startl->line, startc) - st->left + offc;
1660
1661         if(viewc >= 0){
1662                 viewl= txt_get_span(text->lines.first, startl) - st->top + offl;
1663
1664                 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch);
1665                 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch);
1666         }
1667
1668         /* draw closing bracket */
1669         ch= endl->line[endc];
1670         wrap_offset(st, ar, endl, endc, &offl, &offc);
1671         viewc= text_get_char_pos(st, endl->line, endc) - st->left + offc;
1672
1673         if(viewc >= 0) {
1674                 viewl= txt_get_span(text->lines.first, endl) - st->top + offl;
1675
1676                 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch);
1677                 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch);
1678         }
1679 }
1680
1681 /*********************** main area drawing *************************/
1682
1683 void draw_text_main(SpaceText *st, ARegion *ar)
1684 {
1685         Text *text= st->text;
1686         TextLine *tmp;
1687         rcti scroll, back;
1688         char linenr[12];
1689         int i, x, y, winx, linecount= 0, lineno= 0;
1690         int wraplinecount= 0, wrap_skip= 0;
1691
1692         if(st->lheight) st->viewlines= (int)ar->winy/st->lheight;
1693         else st->viewlines= 0;
1694
1695         /* if no text, nothing to do */
1696         if(!text)
1697                 return;
1698         
1699         text_update_drawcache(st, ar);
1700
1701         /* make sure all the positional pointers exist */
1702         if(!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1703                 txt_clean_text(text);
1704         
1705         /* update rects for scroll */
1706         calc_text_rcts(st, ar, &scroll, &back); /* scroll will hold the entire bar size */
1707
1708         /* update syntax formatting if needed */
1709         tmp= text->lines.first;
1710         lineno= 0;
1711         for(i= 0; i<st->top && tmp; i++) {
1712                 if(st->showsyntax && !tmp->format)
1713                         txt_format_line(st, tmp, 0);
1714
1715                 if(st->wordwrap) {
1716                         int lines= text_get_visible_lines_no(st, lineno);
1717
1718                         if (wraplinecount+lines>st->top) {
1719                                 wrap_skip= st->top-wraplinecount;
1720                                 break;
1721                         } else {
1722                                 wraplinecount+= lines;
1723                                 tmp= tmp->next;
1724                                 linecount++;
1725                         }
1726                 } else {
1727                         tmp= tmp->next;
1728                         linecount++;
1729                 }
1730
1731                 lineno++;
1732         }
1733
1734         text_font_begin(st);
1735         st->cwidth= BLF_fixed_width(mono);
1736         st->cwidth= MAX2(st->cwidth, 1);
1737
1738         /* draw line numbers background */
1739         if(st->showlinenrs) {
1740                 x= TXT_OFFSET + TEXTXLOC;
1741
1742                 UI_ThemeColor(TH_GRID);
1743                 glRecti((TXT_OFFSET-12), 0, (TXT_OFFSET-5) + TEXTXLOC, ar->winy - 2);
1744         }
1745         else {
1746                 st->linenrs_tot= 0; /* not used */
1747                 x= TXT_OFFSET;
1748         }
1749         y= ar->winy-st->lheight;
1750         winx= ar->winx - TXT_SCROLL_WIDTH;
1751         
1752         /* draw cursor */
1753         draw_cursor(st, ar);
1754
1755         /* draw the text */
1756         UI_ThemeColor(TH_TEXT);
1757
1758         for(i=0; y>0 && i<st->viewlines && tmp; i++, tmp= tmp->next) {
1759                 if(st->showsyntax && !tmp->format)
1760                         txt_format_line(st, tmp, 0);
1761
1762                 if(st->showlinenrs && !wrap_skip) {
1763                         /* draw line number */
1764                         if(tmp == text->curl)
1765                                 UI_ThemeColor(TH_HILITE);
1766                         else
1767                                 UI_ThemeColor(TH_TEXT);
1768
1769                         sprintf(linenr, "%*d", st->linenrs_tot, i + linecount + 1);
1770                         /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */
1771                         text_font_draw(st, TXT_OFFSET - 7, y, linenr);
1772
1773                         UI_ThemeColor(TH_TEXT);
1774                 }
1775
1776                 if(st->wordwrap) {
1777                         /* draw word wrapped text */
1778                         int lines = text_draw_wrapped(st, tmp->line, x, y, winx-x, tmp->format, wrap_skip);
1779                         y -= lines*st->lheight;
1780                 }
1781                 else {
1782                         /* draw unwrapped text */
1783                         text_draw(st, tmp->line, st->left, ar->winx/st->cwidth, 1, x, y, tmp->format);
1784                         y -= st->lheight;
1785                 }
1786
1787                 wrap_skip= 0;
1788         }
1789         
1790         if(st->flags&ST_SHOW_MARGIN) {
1791                 UI_ThemeColor(TH_HILITE);
1792
1793                 glBegin(GL_LINES);
1794                 glVertex2i(x+st->cwidth*st->margin_column, 0);
1795                 glVertex2i(x+st->cwidth*st->margin_column, ar->winy - 2);
1796                 glEnd();
1797         }
1798
1799         /* draw other stuff */
1800         draw_brackets(st, ar);
1801         draw_markers(st, ar);
1802         glTranslatef(0.375f, 0.375f, 0.0f); /* XXX scroll requires exact pixel space */
1803         draw_textscroll(st, &scroll, &back);
1804         draw_documentation(st, ar);
1805         draw_suggestion_list(st, ar);
1806         
1807         text_font_end(st);
1808 }
1809
1810 /************************** update ***************************/
1811
1812 void text_update_character_width(SpaceText *st)
1813 {
1814         text_font_begin(st);
1815         st->cwidth= BLF_fixed_width(mono);
1816         st->cwidth= MAX2(st->cwidth, 1);
1817         text_font_end(st);
1818 }
1819
1820 /* Moves the view to the cursor location,
1821   also used to make sure the view isnt outside the file */
1822 void text_scroll_to_cursor(SpaceText *st, ScrArea *sa)
1823 {
1824         Text *text;
1825         ARegion *ar= NULL;
1826         int i, x, winx= 0;
1827
1828         if(ELEM3(NULL, st, st->text, st->text->curl)) return;
1829
1830         text= st->text;
1831
1832         for(ar=sa->regionbase.first; ar; ar= ar->next)
1833                 if(ar->regiontype==RGN_TYPE_WINDOW) {
1834                         winx= ar->winx;
1835                         break;
1836                 }
1837         
1838         winx -= TXT_SCROLL_WIDTH;
1839
1840         text_update_character_width(st);
1841
1842         i= txt_get_span(text->lines.first, text->sell);
1843         if(st->wordwrap) {
1844                 int offl, offc;
1845                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1846                 i+= offl;
1847         }
1848
1849         if(st->top+st->viewlines <= i || st->top > i)
1850                 st->top= i - st->viewlines/2;
1851         
1852         if(st->wordwrap) {
1853                 st->left= 0;
1854         }
1855         else {
1856                 x= text_draw(st, text->sell->line, st->left, text->selc, 0, 0, 0, NULL);
1857
1858                 if(x==0 || x>winx)
1859                         st->left= text->curc-0.5*winx/st->cwidth;
1860         }
1861
1862         if(st->top < 0) st->top= 0;
1863         if(st->left <0) st->left= 0;
1864 }
1865
1866 void text_update_cursor_moved(bContext *C)
1867 {
1868         ScrArea *sa= CTX_wm_area(C);
1869         SpaceText *st= CTX_wm_space_text(C);
1870
1871         text_scroll_to_cursor(st, sa);
1872 }