remove assignments which are unused.
[blender.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
40 #include "DNA_text_types.h"
41 #include "DNA_space_types.h"
42 #include "DNA_screen_types.h"
43 #include "DNA_userdef_types.h"
44
45 #include "BKE_context.h"
46 #include "BKE_suggestions.h"
47 #include "BKE_text.h"
48 #include "BKE_utildefines.h"
49
50 #include "BIF_gl.h"
51
52 #include "ED_datafiles.h"
53 #include "UI_interface.h"
54 #include "UI_resources.h"
55
56 #include "text_intern.h"
57
58 /******************** text font drawing ******************/
59 // XXX, fixme
60 #define mono blf_mono_font
61
62 static void text_font_begin(SpaceText *st)
63 {
64         BLF_size(mono, st->lheight, 72);
65 }
66
67 static void text_font_end(SpaceText *UNUSED(st))
68 {
69 }
70
71 static int text_font_draw(SpaceText *UNUSED(st), int x, int y, char *str)
72 {
73         BLF_position(mono, x, y, 0);
74         BLF_draw(mono, str, 65535); /* XXX, use real length */
75
76         return BLF_width(mono, str);
77 }
78
79 static int text_font_draw_character(SpaceText *st, int x, int y, char c)
80 {
81         char str[2];
82         str[0]= c;
83         str[1]= '\0';
84
85         BLF_position(mono, x, y, 0);
86         BLF_draw(mono, str, 1);
87
88         return st->cwidth;
89 }
90
91 int text_font_width(SpaceText *UNUSED(st), char *str)
92 {
93         return BLF_width(mono, str);
94 }
95
96 /****************** flatten string **********************/
97
98 static void flatten_string_append(FlattenString *fs, char c, int accum) 
99 {
100         if(fs->pos>=fs->len && fs->pos>=sizeof(fs->fixedbuf)-1) {
101                 char *nbuf; int *naccum;
102                 if(fs->len) fs->len*= 2;
103                 else fs->len= sizeof(fs->fixedbuf) * 2;
104
105                 nbuf= MEM_callocN(sizeof(*fs->buf)*fs->len, "fs->buf");
106                 naccum= MEM_callocN(sizeof(*fs->accum)*fs->len, "fs->accum");
107
108                 memcpy(nbuf, fs->buf, fs->pos * sizeof(*fs->buf));
109                 memcpy(naccum, fs->accum, fs->pos * sizeof(*fs->accum));
110                 
111                 if(fs->buf != fs->fixedbuf) {
112                         MEM_freeN(fs->buf);
113                         MEM_freeN(fs->accum);
114                 }
115                 
116                 fs->buf= nbuf;
117                 fs->accum= naccum;
118         }
119         
120         fs->buf[fs->pos]= c;    
121         fs->accum[fs->pos]= accum;
122         
123         fs->pos++;
124 }
125
126 int flatten_string(SpaceText *st, FlattenString *fs, char *in)
127 {
128         int r = 0, i = 0;
129
130         memset(fs, 0, sizeof(FlattenString));
131         fs->buf= fs->fixedbuf;
132         fs->accum= fs->fixedaccum;
133         
134         for(r=0, i=0; *in; r++, in++) {
135                 if(*in=='\t') {
136                         if(fs->pos && *(in-1)=='\t')
137                                 i= st->tabnumber;
138                         else if(st->tabnumber > 0)
139                                 i= st->tabnumber - (fs->pos%st->tabnumber);
140
141                         while(i--)
142                                 flatten_string_append(fs, ' ', r);
143                 }
144                 else
145                         flatten_string_append(fs, *in, r);
146         }
147
148         return fs->pos;
149 }
150
151 void flatten_string_free(FlattenString *fs)
152 {
153         if(fs->buf != fs->fixedbuf)
154                 MEM_freeN(fs->buf);
155         if(fs->accum != fs->fixedaccum)
156                 MEM_freeN(fs->accum);
157 }
158
159 /* Checks the specified source string for a Python built-in function name. This
160  name must start at the beginning of the source string and must be followed by
161  a non-identifier (see text_check_identifier(char)) or null character.
162  
163  If a built-in function is found, the length of the matching name is returned.
164  Otherwise, -1 is returned. */
165
166 static int find_builtinfunc(char *string)
167 {
168         int a, i;
169         char builtinfuncs[][9] = {"and", "as", "assert", "break", "class", "continue", "def",
170                                                                 "del", "elif", "else", "except", "exec", "finally",
171                                                                 "for", "from", "global", "if", "import", "in",
172                                                                 "is", "lambda", "not", "or", "pass", "print",
173                                                                 "raise", "return", "try", "while", "yield", "with"};
174
175         for(a=0; a < sizeof(builtinfuncs)/sizeof(builtinfuncs[0]); a++) {
176                 i = 0;
177                 while(1) {
178                         /* If we hit the end of a keyword... (eg. "def") */
179                         if(builtinfuncs[a][i]=='\0') {
180                                 /* If we still have identifier chars in the source (eg. "definate") */
181                                 if(text_check_identifier(string[i]))
182                                         i = -1; /* No match */
183                                 break; /* Next keyword if no match, otherwise we're done */
184                                 
185                         /* If chars mismatch, move on to next keyword */
186                         }
187                         else if(string[i]!=builtinfuncs[a][i]) {
188                                 i = -1;
189                                 break; /* Break inner loop, start next keyword */
190                         }
191                         i++;
192                 }
193                 if(i>0) break; /* If we have a match, we're done */
194         }
195         return i;
196 }
197
198 /* Checks the specified source string for a Python special name. This name must
199  start at the beginning of the source string and must be followed by a non-
200  identifier (see text_check_identifier(char)) or null character.
201  
202  If a special name is found, the length of the matching name is returned.
203  Otherwise, -1 is returned. */
204
205 static int find_specialvar(char *string) 
206 {
207         int i = 0;
208         /* Check for "def" */
209         if(string[0]=='d' && string[1]=='e' && string[2]=='f')
210                 i = 3;
211         /* Check for "class" */
212         else if(string[0]=='c' && string[1]=='l' && string[2]=='a' && string[3]=='s' && string[4]=='s')
213                 i = 5;
214         /* If next source char is an identifier (eg. 'i' in "definate") no match */
215         if(i==0 || text_check_identifier(string[i]))
216                 return -1;
217         return i;
218 }
219
220 static int find_decorator(char *string) 
221 {
222         if(string[0] == '@') {
223                 int i = 1;
224                 while(text_check_identifier(string[i])) {
225                         i++;
226                 }
227                 return i;
228         }
229         return -1;
230 }
231
232 static int find_bool(char *string) 
233 {
234         int i = 0;
235         /* Check for "False" */
236         if(string[0]=='F' && string[1]=='a' && string[2]=='l' && string[3]=='s' && string[4]=='e')
237                 i = 5;
238         /* Check for "True" */
239         else if(string[0]=='T' && string[1]=='r' && string[2]=='u' && string[3]=='e')
240                 i = 4;
241         /* Check for "None" */
242         else if(string[0]=='N' && string[1]=='o' && string[2]=='n' && string[3]=='e')
243                 i = 4;
244         /* If next source char is an identifier (eg. 'i' in "definate") no match */
245         if(i==0 || text_check_identifier(string[i]))
246                 return -1;
247         return i;
248 }
249
250 /* Ensures the format string for the given line is long enough, reallocating
251  as needed. Allocation is done here, alone, to ensure consistency. */
252 int text_check_format_len(TextLine *line, unsigned int len)
253 {
254         if(line->format) {
255                 if(strlen(line->format) < len) {
256                         MEM_freeN(line->format);
257                         line->format = MEM_mallocN(len+2, "SyntaxFormat");
258                         if(!line->format) return 0;
259                 }
260         }
261         else {
262                 line->format = MEM_mallocN(len+2, "SyntaxFormat");
263                 if(!line->format) return 0;
264         }
265
266         return 1;
267 }
268
269 /* Formats the specified line. If do_next is set, the process will move on to
270  the succeeding line if it is affected (eg. multiline strings). Format strings
271  may contain any of the following characters:
272          '_'            Whitespace
273          '#'            Comment text
274          '!'            Punctuation and other symbols
275          'n'            Numerals
276          'l'            String letters
277          'v'            Special variables (class, def)
278          'b'            Built-in names (print, for, etc.)
279          'q'            Other text (identifiers, etc.)
280  It is terminated with a null-terminator '\0' followed by a continuation
281  flag indicating whether the line is part of a multi-line string. */
282
283 static void txt_format_line(SpaceText *st, TextLine *line, int do_next)
284 {
285         FlattenString fs;
286         char *str, *fmt, orig, cont, find, prev = ' ';
287         int len, i;
288
289         /* Get continuation from previous line */
290         if(line->prev && line->prev->format != NULL) {
291                 fmt= line->prev->format;
292                 cont = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
293         }
294         else cont = 0;
295
296         /* Get original continuation from this line */
297         if(line->format != NULL) {
298                 fmt= line->format;
299                 orig = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
300         }
301         else orig = 0xFF;
302
303         flatten_string(st, &fs, line->line);
304         str = fs.buf;
305         len = strlen(str);
306         if(!text_check_format_len(line, len)) {
307                 flatten_string_free(&fs);
308                 return;
309         }
310         fmt = line->format;
311
312         while(*str) {
313                 /* Handle escape sequences by skipping both \ and next char */
314                 if(*str == '\\') {
315                         *fmt = prev; fmt++; str++;
316                         if(*str == '\0') break;
317                         *fmt = prev; fmt++; str++;
318                         continue;
319                 }
320                 /* Handle continuations */
321                 else if(cont) {
322                         /* Triple strings ("""...""" or '''...''') */
323                         if(cont & TXT_TRISTR) {
324                                 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
325                                 if(*str==find && *(str+1)==find && *(str+2)==find) {
326                                         *fmt = 'l'; fmt++; str++;
327                                         *fmt = 'l'; fmt++; str++;
328                                         cont = 0;
329                                 }
330                         /* Handle other strings */
331                         }
332                         else {
333                                 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
334                                 if(*str == find) cont = 0;
335                         }
336
337                         *fmt = 'l';
338                 }
339                 /* Not in a string... */
340                 else {
341                         /* Deal with comments first */
342                         if(prev == '#' || *str == '#')
343                                 *fmt = '#';
344                         /* Strings */
345                         else if(*str == '"' || *str == '\'') {
346                                 find = *str;
347                                 cont = (*str== '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR;
348                                 if(*(str+1) == find && *(str+2) == find) {
349                                         *fmt = 'l'; fmt++; str++;
350                                         *fmt = 'l'; fmt++; str++;
351                                         cont |= TXT_TRISTR;
352                                 }
353                                 *fmt = 'l';
354                         }
355                         /* Whitespace (all ws. has been converted to spaces) */
356                         else if(*str == ' ')
357                                 *fmt = '_';
358                         /* Numbers (digits not part of an identifier and periods followed by digits) */
359                         else if((prev != 'q' && text_check_digit(*str)) || (*str == '.' && text_check_digit(*(str+1))))
360                                 *fmt = 'n';
361                         /* Booleans */
362                         else if(prev != 'q' && (i=find_bool(str)) != -1)
363                                 if(i>0) {
364                                         while(i>1) {
365                                                 *fmt = 'n'; fmt++; str++;
366                                                 i--;
367                                         }
368                                         *fmt = 'n';
369                                 }
370                                 else
371                                         *fmt = 'q';
372                         /* Punctuation */
373                         else if(text_check_delim(*str))
374                                 *fmt = '!';
375                         /* Identifiers and other text (no previous ws. or delims. so text continues) */
376                         else if(prev == 'q')
377                                 *fmt = 'q';
378                         /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
379                         else {
380                                 /* Special vars(v) or built-in keywords(b) */
381                                 if((i=find_specialvar(str)) != -1)
382                                         prev = 'v';
383                                 else if((i=find_builtinfunc(str)) != -1)
384                                         prev = 'b';
385                                 else if((i=find_decorator(str)) != -1)
386                                         prev = 'v'; /* could have a new color for this */
387                                 if(i>0) {
388                                         while(i>1) {
389                                                 *fmt = prev; fmt++; str++;
390                                                 i--;
391                                         }
392                                         *fmt = prev;
393                                 }
394                                 else
395                                         *fmt = 'q';
396                         }
397                 }
398                 prev = *fmt;
399                 fmt++;
400                 str++;
401         }
402
403         /* Terminate and add continuation char */
404         *fmt = '\0'; fmt++;
405         *fmt = cont;
406
407         /* Debugging */
408         //print_format(st, line);
409
410         /* If continuation has changed and we're allowed, process the next line */
411         if(cont!=orig && do_next && line->next) {
412                 txt_format_line(st, line->next, do_next);
413         }
414
415         flatten_string_free(&fs);
416 }
417
418 #if 0
419 /* Formats every line of the current text */
420 static void txt_format_text(SpaceText *st) 
421 {
422         TextLine *linep;
423
424         if(!st->text) return;
425
426         for(linep=st->text->lines.first; linep; linep=linep->next)
427                 txt_format_line(st, linep, 0);
428 }
429 #endif
430
431 /* Sets the current drawing color based on the format character specified */
432 static void format_draw_color(char formatchar)
433 {
434         switch (formatchar) {
435                 case '_': /* Whitespace */
436                         break;
437                 case '!': /* Symbols */
438                         UI_ThemeColorBlend(TH_TEXT, TH_BACK, 0.5f);
439                         break;
440                 case '#': /* Comments */
441                         UI_ThemeColor(TH_SYNTAX_C);
442                         break;
443                 case 'n': /* Numerals */
444                         UI_ThemeColor(TH_SYNTAX_N);
445                         break;
446                 case 'l': /* Strings */
447                         UI_ThemeColor(TH_SYNTAX_L);
448                         break;
449                 case 'v': /* Specials: class, def */
450                         UI_ThemeColor(TH_SYNTAX_V);
451                         break;
452                 case 'b': /* Keywords: for, print, etc. */
453                         UI_ThemeColor(TH_SYNTAX_B);
454                         break;
455                 case 'q': /* Other text (identifiers) */
456                 default:
457                         UI_ThemeColor(TH_TEXT);
458                         break;
459         }
460 }
461
462 /************************** draw text *****************************/
463
464 /***********************/ /*
465
466 Notes on word-wrap
467 --
468 All word-wrap functions follow the algorithm below to maintain consistency.
469         line            The line to wrap (tabs converted to spaces)
470         view_width      The maximum number of characters displayable in the region
471                                 This equals region_width/font_width for the region
472         wrap_chars      Characters that allow wrapping. This equals [' ', '\t', '-']
473
474 def wrap(line, view_width, wrap_chars):
475         draw_start = 0
476         draw_end = view_width
477         pos = 0
478         for c in line:
479                 if pos-draw_start >= view_width:
480                         print line[draw_start:draw_end]
481                         draw_start = draw_end
482                         draw_end += view_width
483                 elif c in wrap_chars:
484                         draw_end = pos+1
485                 pos += 1
486         print line[draw_start:]
487
488 */ /***********************/
489
490 int wrap_width(SpaceText *st, ARegion *ar)
491 {
492         int winx= ar->winx - TXT_SCROLL_WIDTH;
493         int x, max;
494         
495         x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
496         max= st->cwidth ? (winx-x)/st->cwidth : 0;
497         return max>8 ? max : 8;
498 }
499
500 /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
501 void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
502 {
503         Text *text;
504         TextLine *linep;
505         int i, j, start, end, chars, max, chop;
506         char ch;
507
508         *offl= *offc= 0;
509
510         if(!st->text) return;
511         if(!st->wordwrap) return;
512
513         text= st->text;
514
515         /* Move pointer to first visible line (top) */
516         linep= text->lines.first;
517         i= st->top;
518         while(i>0 && linep) {
519                 int lines= text_get_visible_lines(st, ar, linep->line);
520
521                 /* Line before top */
522                 if(linep == linein) {
523                         if(lines <= i)
524                                 /* no visible part of line */
525                                 return;
526                 }
527
528                 if (i-lines<0) {
529                         break;
530                 } else {
531                         linep= linep->next;
532                         (*offl)+= lines-1;
533                         i-= lines;
534                 }
535         }
536
537         max= wrap_width(st, ar);
538
539         while(linep) {
540                 start= 0;
541                 end= max;
542                 chop= 1;
543                 chars= 0;
544                 *offc= 0;
545                 for(i=0, j=0; linep->line[j]!='\0'; j++) {
546
547                         /* Mimic replacement of tabs */
548                         ch= linep->line[j];
549                         if(ch=='\t') {
550                                 chars= st->tabnumber-i%st->tabnumber;
551                                 if(linep==linein && i<cursin) cursin += chars-1;
552                                 ch= ' ';
553                         }
554                         else
555                                 chars= 1;
556
557                         while(chars--) {
558                                 if(i-start>=max) {
559                                         if(chop && linep==linein && i >= cursin) {
560                                                 if (i==cursin) {
561                                                         (*offl)++;
562                                                         *offc -= end-start;
563                                                 }
564
565                                                 return;
566                                         }
567
568                                         (*offl)++;
569                                         *offc -= end-start;
570
571                                         start= end;
572                                         end += max;
573                                         chop= 1;
574                                 }
575                                 else if(ch==' ' || ch=='-') {
576                                         end = i+1;
577                                         chop= 0;
578                                         if(linep==linein && i >= cursin)
579                                                 return;
580                                 }
581                                 i++;
582                         }
583                 }
584                 if(linep==linein) break;
585                 linep= linep->next;
586         }
587 }
588
589 void wrap_offset_in_line(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
590 {
591         int i, j, start, end, chars, max, chop;
592         char ch;
593
594         *offl= *offc= 0;
595
596         if(!st->text) return;
597         if(!st->wordwrap) return;
598
599         max= wrap_width(st, ar);
600
601         start= 0;
602         end= max;
603         chop= 1;
604         chars= 0;
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         DrawCache *drawcache;
1033
1034         text_update_drawcache(st, ar);
1035         drawcache= (DrawCache *)st->drawcache;
1036
1037         if(wrap_top) *wrap_top= 0;
1038
1039         if(st->wordwrap) {
1040                 while(i>0 && pline) {
1041                         int lines= text_get_visible_lines_no(st, lineno);
1042
1043                         if (i-lines<0) {
1044                                 if(wrap_top) *wrap_top= i;
1045                                 break;
1046                         } else {
1047                                 pline= pline->next;
1048                                 i-= lines;
1049                                 lineno++;
1050                         }
1051                 }
1052         } else {
1053                 for(i=st->top; pline->next && i>0; i--)
1054                         pline= pline->next;
1055         }
1056
1057         return pline;
1058 }
1059
1060 /************************ draw scrollbar *****************************/
1061
1062 static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll, rcti *back)
1063 {
1064         int lhlstart, lhlend, ltexth, sell_off, curl_off;
1065         short barheight, barstart, hlstart, hlend, blank_lines;
1066         short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
1067
1068         pix_top_margin = 8;
1069         pix_bottom_margin = 4;
1070         pix_available = ar->winy - pix_top_margin - pix_bottom_margin;
1071         ltexth= text_get_total_lines(st, ar);
1072         blank_lines = st->viewlines / 2;
1073         
1074         /* nicer code: use scroll rect for entire bar */
1075         back->xmin= ar->winx -18;
1076         back->xmax= ar->winx;
1077         back->ymin= 0;
1078         back->ymax= ar->winy;
1079         
1080         scroll->xmin= ar->winx - 17;
1081         scroll->xmax= ar->winx - 5;
1082         scroll->ymin= 4;
1083         scroll->ymax= 4+pix_available;
1084         
1085         /* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */
1086         if(ltexth + blank_lines < st->top + st->viewlines) {
1087                 blank_lines = st->top + st->viewlines - ltexth;
1088         }
1089         
1090         ltexth += blank_lines;
1091
1092         barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0;
1093         pix_bardiff = 0;
1094         if(barheight < 20) {
1095                 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */      
1096                 barheight = 20;
1097         }
1098         barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0;
1099
1100         st->txtbar= *scroll;
1101         st->txtbar.ymax -= barstart;
1102         st->txtbar.ymin = st->txtbar.ymax - barheight;
1103
1104         CLAMP(st->txtbar.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
1105         CLAMP(st->txtbar.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
1106
1107         st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
1108         if(st->pix_per_line<.1) st->pix_per_line=.1f;
1109
1110         curl_off= text_get_span_wrap(st, ar, st->text->lines.first, st->text->curl);
1111         sell_off= text_get_span_wrap(st, ar, st->text->lines.first, st->text->sell);
1112         lhlstart = MIN2(curl_off, sell_off);
1113         lhlend = MAX2(curl_off, sell_off);
1114
1115         if(ltexth > 0) {
1116                 hlstart = (lhlstart * pix_available)/ltexth;
1117                 hlend = (lhlend * pix_available)/ltexth;
1118
1119                 /* the scrollbar is non-linear sized */
1120                 if(pix_bardiff > 0) {
1121                         /* the start of the highlight is in the current viewport */
1122                         if(ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) { 
1123                                 /* speed the progresion of the start of the highlight through the scrollbar */
1124                                 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);       
1125                         }
1126                         else if(lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
1127                                 /* push hl start down */
1128                                 hlstart = barstart + barheight;
1129                         }
1130                         else if(lhlend > st->top  && lhlstart < st->top && hlstart > barstart) {
1131                                 /*fill out start */
1132                                 hlstart = barstart;
1133                         }
1134
1135                         if(hlend <= hlstart) { 
1136                                 hlend = hlstart + 2;
1137                         }
1138
1139                         /* the end of the highlight is in the current viewport */
1140                         if(ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) { 
1141                                 /* speed the progresion of the end of the highlight through the scrollbar */
1142                                 hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines);    
1143                         }
1144                         else if(lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
1145                                 /* push hl end up */
1146                                 hlend = barstart;
1147                         }                                       
1148                         else if(lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
1149                                 /* fill out end */
1150                                 hlend = barstart + barheight;
1151                         }
1152
1153                         if(hlend <= hlstart) { 
1154                                 hlstart = hlend - 2;
1155                         }       
1156                 }       
1157         }
1158         else {
1159                 hlstart = 0;
1160                 hlend = 0;
1161         }
1162
1163         if(hlend - hlstart < 2) { 
1164                 hlend = hlstart + 2;
1165         }
1166         
1167         st->txtscroll= *scroll;
1168         st->txtscroll.ymax= ar->winy - pix_top_margin - hlstart;
1169         st->txtscroll.ymin= ar->winy - pix_top_margin - hlend;
1170
1171         CLAMP(st->txtscroll.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
1172         CLAMP(st->txtscroll.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
1173 }
1174
1175 static void draw_textscroll(SpaceText *st, rcti *scroll, rcti *back)
1176 {
1177         bTheme *btheme= U.themes.first;
1178         uiWidgetColors wcol= btheme->tui.wcol_scroll;
1179         unsigned char col[4];
1180         float rad;
1181         
1182         UI_ThemeColor(TH_BACK);
1183         glRecti(back->xmin, back->ymin, back->xmax, back->ymax);
1184
1185         uiWidgetScrollDraw(&wcol, scroll, &st->txtbar, (st->flags & ST_SCROLL_SELECT)?UI_SCROLL_PRESSED:0);
1186
1187         uiSetRoundBox(15);
1188         rad= 0.4f*MIN2(st->txtscroll.xmax - st->txtscroll.xmin, st->txtscroll.ymax - st->txtscroll.ymin);
1189         UI_GetThemeColor3ubv(TH_HILITE, col);
1190         col[3]= 48;
1191         glColor4ubv(col);
1192         glEnable(GL_BLEND);
1193         uiRoundBox(st->txtscroll.xmin+1, st->txtscroll.ymin, st->txtscroll.xmax-1, st->txtscroll.ymax, rad);
1194         glDisable(GL_BLEND);
1195 }
1196
1197 /************************** draw markers **************************/
1198
1199 static void draw_markers(SpaceText *st, ARegion *ar)
1200 {
1201         Text *text= st->text;
1202         TextMarker *marker, *next;
1203         TextLine *top, *line;
1204         int offl, offc, i, x1, x2, y1, y2, x, y;
1205         int topi, topy;
1206
1207         /* Move pointer to first visible line (top) */
1208         top= first_visible_line(st, ar, NULL);
1209         topi= BLI_findindex(&text->lines, top);
1210
1211         topy= txt_get_span(text->lines.first, top);
1212
1213         for(marker= text->markers.first; marker; marker= next) {
1214                 next= marker->next;
1215
1216                 /* invisible line (before top) */
1217                 if(marker->lineno<topi) continue;
1218
1219                 line= BLI_findlink(&text->lines, marker->lineno);
1220
1221                 /* Remove broken markers */
1222                 if(marker->end>line->len || marker->start>marker->end) {
1223                         BLI_freelinkN(&text->markers, marker);
1224                         continue;
1225                 }
1226
1227                 wrap_offset(st, ar, line, marker->start, &offl, &offc);
1228                 y1 = txt_get_span(top, line) - st->top + offl + topy;
1229                 x1 = text_get_char_pos(st, line->line, marker->start) - st->left + offc;
1230
1231                 wrap_offset(st, ar, line, marker->end, &offl, &offc);
1232                 y2 = txt_get_span(top, line) - st->top + offl + topy;
1233                 x2 = text_get_char_pos(st, line->line, marker->end) - st->left + offc;
1234
1235                 /* invisible part of line (before top, after last visible line) */
1236                 if(y2 < 0 || y1 > st->top+st->viewlines) continue;
1237
1238                 glColor3ubv(marker->color);
1239                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1240                 y= ar->winy-3;
1241
1242                 if(y1==y2) {
1243                         y -= y1*st->lheight;
1244                         glBegin(GL_LINE_LOOP);
1245                         glVertex2i(x+x2*st->cwidth+1, y);
1246                         glVertex2i(x+x1*st->cwidth-2, y);
1247                         glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
1248                         glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
1249                         glEnd();
1250                 }
1251                 else {
1252                         y -= y1*st->lheight;
1253                         glBegin(GL_LINE_STRIP);
1254                         glVertex2i(ar->winx, y);
1255                         glVertex2i(x+x1*st->cwidth-2, y);
1256                         glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
1257                         glVertex2i(ar->winx, y-st->lheight);
1258                         glEnd();
1259                         y-=st->lheight;
1260
1261                         for(i=y1+1; i<y2; i++) {
1262                                 glBegin(GL_LINES);
1263                                 glVertex2i(x, y);
1264                                 glVertex2i(ar->winx, y);
1265                                 glVertex2i(x, y-st->lheight);
1266                                 glVertex2i(ar->winx, y-st->lheight);
1267                                 glEnd();
1268                                 y-=st->lheight;
1269                         }
1270
1271                         glBegin(GL_LINE_STRIP);
1272                         glVertex2i(x, y);
1273                         glVertex2i(x+x2*st->cwidth+1, y);
1274                         glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
1275                         glVertex2i(x, y-st->lheight);
1276                         glEnd();
1277                 }
1278         }
1279 }
1280
1281 /*********************** draw documentation *******************************/
1282
1283 static void draw_documentation(SpaceText *st, ARegion *ar)
1284 {
1285         TextLine *tmp;
1286         char *docs, buf[DOC_WIDTH+1], *p;
1287         int i, br, lines;
1288         int boxw, boxh, l, x, y, top;
1289         
1290         if(!st || !st->text) return;
1291         if(!texttool_text_is_active(st->text)) return;
1292         
1293         docs = texttool_docs_get();
1294
1295         if(!docs) return;
1296
1297         /* Count the visible lines to the cursor */
1298         for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1299         if(l<0) return;
1300         
1301         if(st->showlinenrs) {
1302                 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1303         }
1304         else {
1305                 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4;
1306         }
1307         if(texttool_suggest_first()) {
1308                 x += SUGG_LIST_WIDTH*st->cwidth + 50;
1309         }
1310
1311         top= y= ar->winy - st->lheight*l - 2;
1312         boxw= DOC_WIDTH*st->cwidth + 20;
1313         boxh= (DOC_HEIGHT+1)*st->lheight;
1314
1315         /* Draw panel */
1316         UI_ThemeColor(TH_BACK);
1317         glRecti(x, y, x+boxw, y-boxh);
1318         UI_ThemeColor(TH_SHADE1);
1319         glBegin(GL_LINE_LOOP);
1320         glVertex2i(x, y);
1321         glVertex2i(x+boxw, y);
1322         glVertex2i(x+boxw, y-boxh);
1323         glVertex2i(x, y-boxh);
1324         glEnd();
1325         glBegin(GL_LINE_LOOP);
1326         glVertex2i(x+boxw-10, y-7);
1327         glVertex2i(x+boxw-4, y-7);
1328         glVertex2i(x+boxw-7, y-2);
1329         glEnd();
1330         glBegin(GL_LINE_LOOP);
1331         glVertex2i(x+boxw-10, y-boxh+7);
1332         glVertex2i(x+boxw-4, y-boxh+7);
1333         glVertex2i(x+boxw-7, y-boxh+2);
1334         glEnd();
1335         UI_ThemeColor(TH_TEXT);
1336
1337         i= 0; br= DOC_WIDTH; lines= 0; // XXX -doc_scroll;
1338         for(p=docs; *p; p++) {
1339                 if(*p == '\r' && *(++p) != '\n') *(--p)= '\n'; /* Fix line endings */
1340                 if(*p == ' ' || *p == '\t')
1341                         br= i;
1342                 else if(*p == '\n') {
1343                         buf[i]= '\0';
1344                         if(lines>=0) {
1345                                 y -= st->lheight;
1346                                 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1347                         }
1348                         i= 0; br= DOC_WIDTH; lines++;
1349                 }
1350                 buf[i++]= *p;
1351                 if(i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
1352                         buf[br]= '\0';
1353                         if(lines>=0) {
1354                                 y -= st->lheight;
1355                                 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1356                         }
1357                         p -= i-br-1; /* Rewind pointer to last break */
1358                         i= 0; br= DOC_WIDTH; lines++;
1359                 }
1360                 if(lines >= DOC_HEIGHT) break;
1361         }
1362
1363         if(0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) {
1364                 // XXX doc_scroll--;
1365                 draw_documentation(st, ar);
1366         }
1367 }
1368
1369 /*********************** draw suggestion list *******************************/
1370
1371 static void draw_suggestion_list(SpaceText *st, ARegion *ar)
1372 {
1373         SuggItem *item, *first, *last, *sel;
1374         TextLine *tmp;
1375         char str[SUGG_LIST_WIDTH+1];
1376         int w, boxw=0, boxh, i, l, x, y, b, *top;
1377         
1378         if(!st || !st->text) return;
1379         if(!texttool_text_is_active(st->text)) return;
1380
1381         first = texttool_suggest_first();
1382         last = texttool_suggest_last();
1383
1384         if(!first || !last) return;
1385
1386         text_pop_suggest_list();
1387         sel = texttool_suggest_selected();
1388         top = texttool_suggest_top();
1389
1390         /* Count the visible lines to the cursor */
1391         for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1392         if(l<0) return;
1393         
1394         if(st->showlinenrs) {
1395                 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1396         }
1397         else {
1398                 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4;
1399         }
1400         y = ar->winy - st->lheight*l - 2;
1401
1402         boxw = SUGG_LIST_WIDTH*st->cwidth + 20;
1403         boxh = SUGG_LIST_SIZE*st->lheight + 8;
1404         
1405         UI_ThemeColor(TH_SHADE1);
1406         glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
1407         UI_ThemeColor(TH_BACK);
1408         glRecti(x, y, x+boxw, y-boxh);
1409
1410         /* Set the top 'item' of the visible list */
1411         for(i=0, item=first; i<*top && item->next; i++, item=item->next);
1412
1413         for(i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) {
1414
1415                 y -= st->lheight;
1416
1417                 strncpy(str, item->name, SUGG_LIST_WIDTH);
1418                 str[SUGG_LIST_WIDTH] = '\0';
1419
1420                 w = text_font_width(st, str);
1421                 
1422                 if(item == sel) {
1423                         UI_ThemeColor(TH_SHADE2);
1424                         glRecti(x+16, y-3, x+16+w, y+st->lheight-3);
1425                 }
1426                 b=1; /* b=1 color block, text is default. b=0 no block, color text */
1427                 switch (item->type) {
1428                         case 'k': UI_ThemeColor(TH_SYNTAX_B); b=0; break;
1429                         case 'm': UI_ThemeColor(TH_TEXT); break;
1430                         case 'f': UI_ThemeColor(TH_SYNTAX_L); break;
1431                         case 'v': UI_ThemeColor(TH_SYNTAX_N); break;
1432                         case '?': UI_ThemeColor(TH_TEXT); b=0; break;
1433                 }
1434                 if(b) {
1435                         glRecti(x+8, y+2, x+11, y+5);
1436                         UI_ThemeColor(TH_TEXT);
1437                 }
1438                 text_draw(st, str, 0, 0, 1, x+16, y-1, NULL);
1439
1440                 if(item == last) break;
1441         }
1442 }
1443
1444 /*********************** draw cursor ************************/
1445
1446 static void draw_cursor(SpaceText *st, ARegion *ar)
1447 {
1448         Text *text= st->text;
1449         int vcurl, vcurc, vsell, vselc, hidden=0;
1450         int x, y, w, i;
1451
1452         /* Draw the selection */
1453         if(text->curl!=text->sell || text->curc!=text->selc) {
1454                 int offl, offc;
1455                 /* Convert all to view space character coordinates */
1456                 wrap_offset(st, ar, text->curl, text->curc, &offl, &offc);
1457                 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
1458                 vcurc = text_get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
1459                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1460                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1461                 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1462
1463                 if(vcurc<0) vcurc=0;
1464                 if(vselc<0) vselc=0, hidden=1;
1465                 
1466                 UI_ThemeColor(TH_SHADE2);
1467                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1468                 y= ar->winy-2;
1469
1470                 if(vcurl==vsell) {
1471                         y -= vcurl*st->lheight;
1472                         if(vcurc < vselc)
1473                                 glRecti(x+vcurc*st->cwidth-1, y, x+vselc*st->cwidth, y-st->lheight);
1474                         else
1475                                 glRecti(x+vselc*st->cwidth-1, y, x+vcurc*st->cwidth, y-st->lheight);
1476                 }
1477                 else {
1478                         int froml, fromc, tol, toc;
1479
1480                         if(vcurl < vsell) {
1481                                 froml= vcurl; tol= vsell;
1482                                 fromc= vcurc; toc= vselc;
1483                         }
1484                         else {
1485                                 froml= vsell; tol= vcurl;
1486                                 fromc= vselc; toc= vcurc;
1487                         }
1488
1489                         y -= froml*st->lheight;
1490                         glRecti(x+fromc*st->cwidth-1, y, ar->winx, y-st->lheight); y-=st->lheight;
1491                         for(i=froml+1; i<tol; i++)
1492                                 glRecti(x-4, y, ar->winx, y-st->lheight),  y-=st->lheight;
1493
1494                         glRecti(x-4, y, x+toc*st->cwidth, y-st->lheight);  y-=st->lheight;
1495                 }
1496         }
1497         else {
1498                 int offl, offc;
1499                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1500                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1501                 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1502
1503                 if(vselc<0) {
1504                         vselc= 0;
1505                         hidden= 1;
1506                 }
1507         }
1508
1509         if(st->line_hlight) {
1510                 int x1, x2, y1, y2;
1511
1512                 if(st->wordwrap) {
1513                         int visible_lines = text_get_visible_lines(st, ar, text->sell->line);
1514                         int offl, offc;
1515
1516                         wrap_offset_in_line(st, ar, text->sell, text->selc, &offl, &offc);
1517
1518                         y1= ar->winy-2 - (vsell-offl)*st->lheight;
1519                         y2= y1-st->lheight*visible_lines+1;
1520                 } else {
1521                         y1= ar->winy-2 - vsell*st->lheight;
1522                         y2= y1-st->lheight+1;
1523                 }
1524
1525                 if(!(y1<0 || y2 > ar->winy)) { /* check we need to draw */
1526                         x1= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1527                         x2= x1 + ar->winx;
1528
1529                         glColor4ub(255, 255, 255, 32);
1530                         
1531                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1532                         glEnable(GL_BLEND);
1533                         glRecti(x1-4, y1, x2, y2);
1534                         glDisable(GL_BLEND);
1535                 }
1536         }
1537         
1538         if(!hidden) {
1539                 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
1540                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1541                 x += vselc*st->cwidth;
1542                 y= ar->winy-2 - vsell*st->lheight;
1543                 
1544                 if(st->overwrite) {
1545                         char ch= text->sell->line[text->selc];
1546                         
1547                         w= st->cwidth;
1548                         if(ch=='\t')  w*= st->tabnumber-(vselc+st->left)%st->tabnumber;
1549                         
1550                         UI_ThemeColor(TH_HILITE);
1551                         glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1);
1552                 }
1553                 else {
1554                         UI_ThemeColor(TH_HILITE);
1555                         glRecti(x-1, y, x+1, y-st->lheight);
1556                 }
1557         }
1558 }
1559
1560 /******************* draw matching brackets *********************/
1561
1562 static void draw_brackets(SpaceText *st, ARegion *ar)
1563 {
1564         TextLine *startl, *endl, *linep;
1565         Text *text = st->text;
1566         int b, c, startc, endc, find, stack;
1567         int viewc, viewl, offl, offc, x, y;
1568         char ch;
1569
1570         // showsyntax must be on or else the format string will be null
1571         if(!text->curl || !st->showsyntax) return;
1572
1573         startl= text->curl;
1574         startc= text->curc;
1575         b= text_check_bracket(startl->line[startc]);
1576         if(b==0 && startc>0) b = text_check_bracket(startl->line[--startc]);
1577         if(b==0) return;
1578         
1579         linep= startl;
1580         c= startc;
1581         endl= NULL;
1582         endc= -1;
1583         find= -b;
1584         stack= 0;
1585         
1586         /* Dont highlight backets if syntax HL is off or bracket in string or comment. */
1587         if(!linep->format || linep->format[c] == 'l' || linep->format[c] == '#')
1588                 return;
1589
1590         if(b>0) {
1591                 /* opening bracket, search forward for close */
1592                 c++;
1593                 while(linep) {
1594                         while(c<linep->len) {
1595                                 if(linep->format && linep->format[c] != 'l' && linep->format[c] != '#') {
1596                                         b= text_check_bracket(linep->line[c]);
1597                                         if(b==find) {
1598                                                 if(stack==0) {
1599                                                         endl= linep;
1600                                                         endc= c;
1601                                                         break;
1602                                                 }
1603                                                 stack--;
1604                                         }
1605                                         else if(b==-find) {
1606                                                 stack++;
1607                                         }
1608                                 }
1609                                 c++;
1610                         }
1611                         if(endl) break;
1612                         linep= linep->next;
1613                         c= 0;
1614                 }
1615         }
1616         else {
1617                 /* closing bracket, search backward for open */
1618                 c--;
1619                 while(linep) {
1620                         while(c>=0) {
1621                                 if(linep->format && linep->format[c] != 'l' && linep->format[c] != '#') {
1622                                         b= text_check_bracket(linep->line[c]);
1623                                         if(b==find) {
1624                                                 if(stack==0) {
1625                                                         endl= linep;
1626                                                         endc= c;
1627                                                         break;
1628                                                 }
1629                                                 stack--;
1630                                         }
1631                                         else if(b==-find) {
1632                                                 stack++;
1633                                         }
1634                                 }
1635                                 c--;
1636                         }
1637                         if(endl) break;
1638                         linep= linep->prev;
1639                         if(linep) c= linep->len-1;
1640                 }
1641         }
1642
1643         if(!endl || endc==-1)
1644                 return;
1645
1646         UI_ThemeColor(TH_HILITE);       
1647         x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1648         y= ar->winy - st->lheight;
1649
1650         /* draw opening bracket */
1651         ch= startl->line[startc];
1652         wrap_offset(st, ar, startl, startc, &offl, &offc);
1653         viewc= text_get_char_pos(st, startl->line, startc) - st->left + offc;
1654
1655         if(viewc >= 0){
1656                 viewl= txt_get_span(text->lines.first, startl) - st->top + offl;
1657
1658                 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch);
1659                 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch);
1660         }
1661
1662         /* draw closing bracket */
1663         ch= endl->line[endc];
1664         wrap_offset(st, ar, endl, endc, &offl, &offc);
1665         viewc= text_get_char_pos(st, endl->line, endc) - st->left + offc;
1666
1667         if(viewc >= 0) {
1668                 viewl= txt_get_span(text->lines.first, endl) - st->top + offl;
1669
1670                 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch);
1671                 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch);
1672         }
1673 }
1674
1675 /*********************** main area drawing *************************/
1676
1677 void draw_text_main(SpaceText *st, ARegion *ar)
1678 {
1679         Text *text= st->text;
1680         TextLine *tmp;
1681         rcti scroll, back;
1682         char linenr[12];
1683         int i, x, y, winx, linecount= 0, lineno= 0;
1684         int wraplinecount= 0, wrap_skip= 0;
1685
1686         /* if no text, nothing to do */
1687         if(!text)
1688                 return;
1689         
1690         text_update_drawcache(st, ar);
1691
1692         /* make sure all the positional pointers exist */
1693         if(!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1694                 txt_clean_text(text);
1695         
1696         if(st->lheight) st->viewlines= (int)ar->winy/st->lheight;
1697         else st->viewlines= 0;
1698         
1699         /* update rects for scroll */
1700         calc_text_rcts(st, ar, &scroll, &back); /* scroll will hold the entire bar size */
1701
1702         /* update syntax formatting if needed */
1703         tmp= text->lines.first;
1704         lineno= 0;
1705         for(i= 0; i<st->top && tmp; i++) {
1706                 if(st->showsyntax && !tmp->format)
1707                         txt_format_line(st, tmp, 0);
1708
1709                 if(st->wordwrap) {
1710                         int lines= text_get_visible_lines_no(st, lineno);
1711
1712                         if (wraplinecount+lines>st->top) {
1713                                 wrap_skip= st->top-wraplinecount;
1714                                 break;
1715                         } else {
1716                                 wraplinecount+= lines;
1717                                 tmp= tmp->next;
1718                                 linecount++;
1719                         }
1720                 } else {
1721                         tmp= tmp->next;
1722                         linecount++;
1723                 }
1724
1725                 lineno++;
1726         }
1727
1728         text_font_begin(st);
1729         st->cwidth= BLF_fixed_width(mono);
1730         st->cwidth= MAX2(st->cwidth, 1);
1731
1732         /* draw line numbers background */
1733         if(st->showlinenrs) {
1734                 x= TXT_OFFSET + TEXTXLOC;
1735
1736                 UI_ThemeColor(TH_GRID);
1737                 glRecti((TXT_OFFSET-12), 0, (TXT_OFFSET-5) + TEXTXLOC, ar->winy - 2);
1738         }
1739         else {
1740                 st->linenrs_tot= 0; /* not used */
1741                 x= TXT_OFFSET;
1742         }
1743         y= ar->winy-st->lheight;
1744         winx= ar->winx - TXT_SCROLL_WIDTH;
1745         
1746         /* draw cursor */
1747         draw_cursor(st, ar);
1748
1749         /* draw the text */
1750         UI_ThemeColor(TH_TEXT);
1751
1752         for(i=0; y>0 && i<st->viewlines && tmp; i++, tmp= tmp->next) {
1753                 if(st->showsyntax && !tmp->format)
1754                         txt_format_line(st, tmp, 0);
1755
1756                 if(st->showlinenrs && !wrap_skip) {
1757                         /* draw line number */
1758                         if(tmp == text->curl)
1759                                 UI_ThemeColor(TH_HILITE);
1760                         else
1761                                 UI_ThemeColor(TH_TEXT);
1762
1763                         sprintf(linenr, "%d", i + linecount + 1);
1764                         /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */
1765                         text_font_draw(st, TXT_OFFSET - 7, y, linenr);
1766
1767                         UI_ThemeColor(TH_TEXT);
1768                 }
1769
1770                 if(st->wordwrap) {
1771                         /* draw word wrapped text */
1772                         int lines = text_draw_wrapped(st, tmp->line, x, y, winx-x, tmp->format, wrap_skip);
1773                         y -= lines*st->lheight;
1774                 }
1775                 else {
1776                         /* draw unwrapped text */
1777                         text_draw(st, tmp->line, st->left, ar->winx/st->cwidth, 1, x, y, tmp->format);
1778                         y -= st->lheight;
1779                 }
1780
1781                 wrap_skip= 0;
1782         }
1783         
1784         /* draw other stuff */
1785         draw_brackets(st, ar);
1786         draw_markers(st, ar);
1787         glTranslatef(0.375f, 0.375f, 0.0f); /* XXX scroll requires exact pixel space */
1788         draw_textscroll(st, &scroll, &back);
1789         draw_documentation(st, ar);
1790         draw_suggestion_list(st, ar);
1791         
1792         text_font_end(st);
1793 }
1794
1795 /************************** update ***************************/
1796
1797 void text_update_character_width(SpaceText *st)
1798 {
1799         text_font_begin(st);
1800         st->cwidth= BLF_fixed_width(mono);
1801         st->cwidth= MAX2(st->cwidth, 1);
1802         text_font_end(st);
1803 }
1804
1805 /* Moves the view to the cursor location,
1806   also used to make sure the view isnt outside the file */
1807 void text_update_cursor_moved(bContext *C)
1808 {
1809         ScrArea *sa= CTX_wm_area(C);
1810         SpaceText *st= CTX_wm_space_text(C);
1811         Text *text;
1812         ARegion *ar;
1813         int i, x, winx= 0;
1814
1815         if(ELEM3(NULL, st, st->text, st->text->curl)) return;
1816
1817         text= st->text;
1818
1819         for(ar=sa->regionbase.first; ar; ar= ar->next)
1820                 if(ar->regiontype==RGN_TYPE_WINDOW)
1821                         winx= ar->winx;
1822         
1823         winx -= TXT_SCROLL_WIDTH;
1824
1825         text_update_character_width(st);
1826
1827         i= txt_get_span(text->lines.first, text->sell);
1828         if(st->wordwrap) {
1829                 int offl, offc;
1830                 wrap_offset(st, CTX_wm_region(C), text->sell, text->selc, &offl, &offc);
1831                 i+= offl;
1832         }
1833
1834         if(st->top+st->viewlines <= i || st->top > i)
1835                 st->top= i - st->viewlines/2;
1836         
1837         if(st->wordwrap) {
1838                 st->left= 0;
1839         }
1840         else {
1841                 x= text_draw(st, text->sell->line, st->left, text->selc, 0, 0, 0, NULL);
1842
1843                 if(x==0 || x>winx)
1844                         st->left= text->curc-0.5*winx/st->cwidth;
1845         }
1846
1847         if(st->top < 0) st->top= 0;
1848         if(st->left <0) st->left= 0;
1849 }
1850