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