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