PyConsole improvements
[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  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <math.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/stat.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLF_api.h"
38
39 #include "BLI_blenlib.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_global.h"
47 #include "BKE_main.h"
48 #include "BKE_suggestions.h"
49 #include "BKE_text.h"
50 #include "BKE_utildefines.h"
51
52 #include "BIF_gl.h"
53 #include "BIF_glutil.h"
54
55 #include "ED_datafiles.h"
56 #include "UI_interface.h"
57 #include "UI_resources.h"
58
59 #include "text_intern.h"
60
61 /******************** text font drawing ******************/
62
63 static void text_font_begin(SpaceText *st)
64 {
65         static int mono= -1; // XXX needs proper storage
66
67         if(mono == -1)
68                 mono= BLF_load_mem("monospace", (unsigned char*)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
69
70         BLF_set(mono);
71         BLF_aspect(1.0);
72
73         BLF_size(st->lheight, 72);
74 }
75
76 static void text_font_end(SpaceText *st)
77 {
78 }
79
80 static int text_font_draw(SpaceText *st, int x, int y, char *str)
81 {
82         BLF_position(x, y, 0);
83         BLF_draw(str);
84
85         return BLF_width(str);
86 }
87
88 static int text_font_draw_character(SpaceText *st, int x, int y, char c)
89 {
90         char str[2];
91
92         str[0]= c;
93         str[1]= '\0';
94
95         BLF_position(x, y, 0);
96         BLF_draw(str);
97
98         return st->cwidth;
99 }
100
101 int text_font_width(SpaceText *st, char *str)
102 {
103         return BLF_width(str);
104 }
105
106 /****************** flatten string **********************/
107
108 static void flatten_string_append(FlattenString *fs, char c, int accum) 
109 {
110         if(fs->pos>=fs->len && fs->pos>=sizeof(fs->fixedbuf)-1) {
111                 char *nbuf; int *naccum;
112                 if(fs->len) fs->len*= 2;
113                 else fs->len= sizeof(fs->fixedbuf) * 2;
114
115                 nbuf= MEM_callocN(sizeof(*fs->buf)*fs->len, "fs->buf");
116                 naccum= MEM_callocN(sizeof(*fs->accum)*fs->len, "fs->accum");
117
118                 memcpy(nbuf, fs->buf, fs->pos);
119                 memcpy(naccum, fs->accum, fs->pos);
120                 
121                 if(fs->buf != fs->fixedbuf) {
122                         MEM_freeN(fs->buf);
123                         MEM_freeN(fs->accum);
124                 }
125                 
126                 fs->buf= nbuf;
127                 fs->accum= naccum;
128         }
129         
130         fs->buf[fs->pos]= c;    
131         fs->accum[fs->pos]= accum;
132         
133         fs->pos++;
134 }
135
136 int flatten_string(SpaceText *st, FlattenString *fs, char *in)
137 {
138         int r = 0, i = 0;
139
140         memset(fs, 0, sizeof(FlattenString));
141         fs->buf= fs->fixedbuf;
142         fs->accum= fs->fixedaccum;
143         
144         for(r=0, i=0; *in; r++, in++) {
145                 if(*in=='\t') {
146                         if(fs->pos && *(in-1)=='\t')
147                                 i= st->tabnumber;
148                         else if(st->tabnumber > 0)
149                                 i= st->tabnumber - (fs->pos%st->tabnumber);
150
151                         while(i--)
152                                 flatten_string_append(fs, ' ', r);
153                 }
154                 else
155                         flatten_string_append(fs, *in, r);
156         }
157
158         return fs->pos;
159 }
160
161 void flatten_string_free(FlattenString *fs)
162 {
163         if(fs->buf != fs->fixedbuf)
164                 MEM_freeN(fs->buf);
165         if(fs->accum != fs->fixedaccum)
166                 MEM_freeN(fs->accum);
167 }
168
169 /* Checks the specified source string for a Python built-in function name. This
170  name must start at the beginning of the source string and must be followed by
171  a non-identifier (see text_check_identifier(char)) or null character.
172  
173  If a built-in function is found, the length of the matching name is returned.
174  Otherwise, -1 is returned. */
175
176 static int find_builtinfunc(char *string)
177 {
178         int a, i;
179         char builtinfuncs[][11] = {"and", "as", "assert", "break", "class", "continue", "def",
180                                                                 "del", "elif", "else", "except", "exec", "finally",
181                                                                 "for", "from", "global", "if", "import", "in",
182                                                                 "is", "lambda", "not", "or", "pass", "print",
183                                                                 "raise", "return", "try", "while", "yield"};
184         for(a=0; a<30; a++) {
185                 i = 0;
186                 while(1) {
187                         /* If we hit the end of a keyword... (eg. "def") */
188                         if(builtinfuncs[a][i]=='\0') {
189                                 /* If we still have identifier chars in the source (eg. "definate") */
190                                 if(text_check_identifier(string[i]))
191                                         i = -1; /* No match */
192                                 break; /* Next keyword if no match, otherwise we're done */
193                                 
194                         /* If chars mismatch, move on to next keyword */
195                         }
196                         else if(string[i]!=builtinfuncs[a][i]) {
197                                 i = -1;
198                                 break; /* Break inner loop, start next keyword */
199                         }
200                         i++;
201                 }
202                 if(i>0) break; /* If we have a match, we're done */
203         }
204         return i;
205 }
206
207 /* Checks the specified source string for a Python special name. This name must
208  start at the beginning of the source string and must be followed by a non-
209  identifier (see text_check_identifier(char)) or null character.
210  
211  If a special name is found, the length of the matching name is returned.
212  Otherwise, -1 is returned. */
213
214 static int find_specialvar(char *string) 
215 {
216         int i = 0;
217         /* Check for "def" */
218         if(string[0]=='d' && string[1]=='e' && string[2]=='f')
219                 i = 3;
220         /* Check for "class" */
221         else if(string[0]=='c' && string[1]=='l' && string[2]=='a' && string[3]=='s' && string[4]=='s')
222                 i = 5;
223         /* If next source char is an identifier (eg. 'i' in "definate") no match */
224         if(i==0 || text_check_identifier(string[i]))
225                 return -1;
226         return i;
227 }
228
229 /* Ensures the format string for the given line is long enough, reallocating
230  as needed. Allocation is done here, alone, to ensure consistency. */
231 int text_check_format_len(TextLine *line, unsigned int len)
232 {
233         if(line->format) {
234                 if(strlen(line->format) < len) {
235                         MEM_freeN(line->format);
236                         line->format = MEM_mallocN(len+2, "SyntaxFormat");
237                         if(!line->format) return 0;
238                 }
239         }
240         else {
241                 line->format = MEM_mallocN(len+2, "SyntaxFormat");
242                 if(!line->format) return 0;
243         }
244
245         return 1;
246 }
247
248 /* Formats the specified line. If do_next is set, the process will move on to
249  the succeeding line if it is affected (eg. multiline strings). Format strings
250  may contain any of the following characters:
251         '_'             Whitespace
252         '#'             Comment text
253         '!'             Punctuation and other symbols
254         'n'             Numerals
255         'l'             String letters
256         'v'             Special variables (class, def)
257         'b'             Built-in names (print, for, etc.)
258         'q'             Other text (identifiers, etc.)
259  It is terminated with a null-terminator '\0' followed by a continuation
260  flag indicating whether the line is part of a multi-line string. */
261
262 static void txt_format_line(SpaceText *st, TextLine *line, int do_next)
263 {
264         FlattenString fs;
265         char *str, *fmt, orig, cont, find, prev = ' ';
266         int len, i;
267
268         /* Get continuation from previous line */
269         if(line->prev && line->prev->format != NULL) {
270                 fmt= line->prev->format;
271                 cont = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
272         }
273         else cont = 0;
274
275         /* Get original continuation from this line */
276         if(line->format != NULL) {
277                 fmt= line->format;
278                 orig = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
279         }
280         else orig = 0xFF;
281
282         flatten_string(st, &fs, line->line);
283         str = fs.buf;
284         len = strlen(str);
285         if(!text_check_format_len(line, len)) {
286                 flatten_string_free(&fs);
287                 return;
288         }
289         fmt = line->format;
290
291         while(*str) {
292                 /* Handle escape sequences by skipping both \ and next char */
293                 if(*str == '\\') {
294                         *fmt = prev; fmt++; str++;
295                         if(*str == '\0') break;
296                         *fmt = prev; fmt++; str++;
297                         continue;
298                 }
299                 /* Handle continuations */
300                 else if(cont) {
301                         /* Triple strings ("""...""" or '''...''') */
302                         if(cont & TXT_TRISTR) {
303                                 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
304                                 if(*str==find && *(str+1)==find && *(str+2)==find) {
305                                         *fmt = 'l'; fmt++; str++;
306                                         *fmt = 'l'; fmt++; str++;
307                                         cont = 0;
308                                 }
309                         /* Handle other strings */
310                         }
311                         else {
312                                 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
313                                 if(*str == find) cont = 0;
314                         }
315
316                         *fmt = 'l';
317                 }
318                 /* Not in a string... */
319                 else {
320                         /* Deal with comments first */
321                         if(prev == '#' || *str == '#')
322                                 *fmt = '#';
323                         /* Strings */
324                         else if(*str == '"' || *str == '\'') {
325                                 find = *str;
326                                 cont = (*str== '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR;
327                                 if(*(str+1) == find && *(str+2) == find) {
328                                         *fmt = 'l'; fmt++; str++;
329                                         *fmt = 'l'; fmt++; str++;
330                                         cont |= TXT_TRISTR;
331                                 }
332                                 *fmt = 'l';
333                         }
334                         /* Whitespace (all ws. has been converted to spaces) */
335                         else if(*str == ' ')
336                                 *fmt = '_';
337                         /* Numbers (digits not part of an identifier and periods followed by digits) */
338                         else if((prev != 'q' && text_check_digit(*str)) || (*str == '.' && text_check_digit(*(str+1))))
339                                 *fmt = 'n';
340                         /* Punctuation */
341                         else if(text_check_delim(*str))
342                                 *fmt = '!';
343                         /* Identifiers and other text (no previous ws. or delims. so text continues) */
344                         else if(prev == 'q')
345                                 *fmt = 'q';
346                         /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
347                         else {
348                                 /* Special vars(v) or built-in keywords(b) */
349                                 if((i=find_specialvar(str)) != -1)
350                                         prev = 'v';
351                                 else if((i=find_builtinfunc(str)) != -1)
352                                         prev = 'b';
353                                 if(i>0) {
354                                         while(i>1) {
355                                                 *fmt = prev; fmt++; str++;
356                                                 i--;
357                                         }
358                                         *fmt = prev;
359                                 }
360                                 else
361                                         *fmt = 'q';
362                         }
363                 }
364                 prev = *fmt;
365                 fmt++;
366                 str++;
367         }
368
369         /* Terminate and add continuation char */
370         *fmt = '\0'; fmt++;
371         *fmt = cont;
372
373         /* Debugging */
374         //print_format(st, line);
375
376         /* If continuation has changed and we're allowed, process the next line */
377         if(cont!=orig && do_next && line->next) {
378                 txt_format_line(st, line->next, do_next);
379         }
380
381         flatten_string_free(&fs);
382 }
383
384 #if 0
385 /* Formats every line of the current text */
386 static void txt_format_text(SpaceText *st) 
387 {
388         TextLine *linep;
389
390         if(!st->text) return;
391
392         for(linep=st->text->lines.first; linep; linep=linep->next)
393                 txt_format_line(st, linep, 0);
394 }
395 #endif
396
397 /* Sets the current drawing color based on the format character specified */
398 static void format_draw_color(char formatchar)
399 {
400         switch (formatchar) {
401                 case '_': /* Whitespace */
402                         break;
403                 case '!': /* Symbols */
404                         UI_ThemeColorBlend(TH_TEXT, TH_BACK, 0.5f);
405                         break;
406                 case '#': /* Comments */
407                         UI_ThemeColor(TH_SYNTAX_C);
408                         break;
409                 case 'n': /* Numerals */
410                         UI_ThemeColor(TH_SYNTAX_N);
411                         break;
412                 case 'l': /* Strings */
413                         UI_ThemeColor(TH_SYNTAX_L);
414                         break;
415                 case 'v': /* Specials: class, def */
416                         UI_ThemeColor(TH_SYNTAX_V);
417                         break;
418                 case 'b': /* Keywords: for, print, etc. */
419                         UI_ThemeColor(TH_SYNTAX_B);
420                         break;
421                 case 'q': /* Other text (identifiers) */
422                 default:
423                         UI_ThemeColor(TH_TEXT);
424                         break;
425         }
426 }
427
428 /************************** draw text *****************************/
429
430 /***********************/ /*
431
432 Notes on word-wrap
433 --
434 All word-wrap functions follow the algorithm below to maintain consistency.
435         line            The line to wrap (tabs converted to spaces)
436         view_width      The maximum number of characters displayable in the region
437                                 This equals region_width/font_width for the region
438         wrap_chars      Characters that allow wrapping. This equals [' ', '\t', '-']
439
440 def wrap(line, view_width, wrap_chars):
441         draw_start = 0
442         draw_end = view_width
443         pos = 0
444         for c in line:
445                 if pos-draw_start >= view_width:
446                         print line[draw_start:draw_end]
447                         draw_start = draw_end
448                         draw_end += view_width
449                 elif c in wrap_chars:
450                         draw_end = pos+1
451                 pos += 1
452         print line[draw_start:]
453
454 */ /***********************/
455
456 int wrap_width(SpaceText *st, ARegion *ar)
457 {
458         int x, max;
459         
460         x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
461         max= (ar->winx-x)/st->cwidth;
462         return max>8 ? max : 8;
463 }
464
465 /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
466 void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
467 {
468         Text *text;
469         TextLine *linep;
470         int i, j, start, end, chars, max, chop;
471         char ch;
472
473         *offl= *offc= 0;
474
475         if(!st->text) return;
476         if(!st->wordwrap) return;
477
478         text= st->text;
479
480         /* Move pointer to first visible line (top) */
481         linep= text->lines.first;
482         i= st->top;
483         while(i>0 && linep) {
484                 if(linep == linein) return; /* Line before top */
485                 linep= linep->next;
486                 i--;
487         }
488
489         max= wrap_width(st, ar);
490
491         while(linep) {
492                 start= 0;
493                 end= max;
494                 chop= 1;
495                 chars= 0;
496                 *offc= 0;
497                 for(i=0, j=0; linep->line[j]!='\0'; j++) {
498
499                         /* Mimic replacement of tabs */
500                         ch= linep->line[j];
501                         if(ch=='\t') {
502                                 chars= st->tabnumber-i%st->tabnumber;
503                                 if(linep==linein && i<cursin) cursin += chars-1;
504                                 ch= ' ';
505                         }
506                         else
507                                 chars= 1;
508
509                         while(chars--) {
510                                 if(i-start>=max) {
511                                         if(chop && linep==linein && i >= cursin)
512                                                 return;
513                                         (*offl)++;
514                                         *offc -= end-start;
515                                         start= end;
516                                         end += max;
517                                         chop= 1;
518                                 }
519                                 else if(ch==' ' || ch=='-') {
520                                         end = i+1;
521                                         chop= 0;
522                                         if(linep==linein && i >= cursin)
523                                                 return;
524                                 }
525                                 i++;
526                         }
527                 }
528                 if(linep==linein) break;
529                 linep= linep->next;
530         }
531 }
532
533 static int get_char_pos(SpaceText *st, char *line, int cur)
534 {
535         int a=0, i;
536         
537         for(i=0; i<cur && line[i]; i++) {
538                 if(line[i]=='\t')
539                         a += st->tabnumber-a%st->tabnumber;
540                 else
541                         a++;
542         }
543         return a;
544 }
545
546 static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format)
547 {
548         FlattenString fs;
549         int basex, i, a, len, start, end, max, lines;
550         
551         len= flatten_string(st, &fs, str);
552         str= fs.buf;
553         max= w/st->cwidth;
554         if(max<8) max= 8;
555         basex= x;
556
557         lines= 1;
558         start= 0;
559         end= max;
560         for(i=0; i<len; i++) {
561                 if(i-start >= max) {
562                         /* Draw the visible portion of text on the overshot line */
563                         for(a=start; a<end; a++) {
564                                 if(st->showsyntax && format) format_draw_color(format[a]);
565                                 x += text_font_draw_character(st, x, y, str[a]);
566                         }
567                         y -= st->lheight;
568                         x= basex;
569                         lines++;
570                         start= end;
571                         end += max;
572                 }
573                 else if(str[i]==' ' || str[i]=='-') {
574                         end = i+1;
575                 }
576         }
577
578         /* Draw the remaining text */
579         for(a=start; a<len; a++) {
580                 if(st->showsyntax && format)
581                         format_draw_color(format[a]);
582
583                 x += text_font_draw_character(st, x, y, str[a]);
584         }
585
586         flatten_string_free(&fs);
587
588         return lines;
589 }
590
591 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format)
592 {
593         FlattenString fs;
594         int r=0, w= 0;
595         int *acc;
596         char *in;
597
598         w= flatten_string(st, &fs, str);
599         if(w < cshift) {
600                 flatten_string_free(&fs);
601                 return 0; /* String is shorter than shift */
602         }
603         
604         in= fs.buf+cshift;
605         acc= fs.accum+cshift;
606         w= w-cshift;
607
608         if(draw) {
609                 if(st->showsyntax && format) {
610                         int amount, a;
611                         format = format+cshift;
612                 
613                         amount = strlen(in);
614                         
615                         for(a = 0; a < amount; a++) {
616                                 format_draw_color(format[a]);
617                                 x += text_font_draw_character(st, x, y, in[a]);
618                         }
619                 }
620                 else
621                         text_font_draw(st, x, y, in);
622         }
623         else {
624                 while(w-- && *acc++ < maxwidth)
625                         r+= st->cwidth;
626         }
627
628         flatten_string_free(&fs);
629
630         if(cshift && r==0)
631                 return 0;
632         else if(st->showlinenrs)
633                 return r+TXT_OFFSET+TEXTXLOC;
634         else
635                 return r+TXT_OFFSET;
636 }
637
638 /************************ draw scrollbar *****************************/
639
640 static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll)
641 {
642         int lhlstart, lhlend, ltexth;
643         short barheight, barstart, hlstart, hlend, blank_lines;
644         short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
645
646         pix_top_margin = 8;
647         pix_bottom_margin = 4;
648         pix_available = ar->winy - pix_top_margin - pix_bottom_margin;
649         ltexth= txt_get_span(st->text->lines.first, st->text->lines.last);
650         blank_lines = st->viewlines / 2;
651         
652         /* nicer code: use scroll rect for entire bar */
653         scroll->xmin= 5;
654         scroll->xmax= 17;
655         scroll->ymin= 4;
656         scroll->ymax= 4+pix_available;
657         
658         /* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */
659         if(ltexth + blank_lines < st->top + st->viewlines) {
660                 blank_lines = st->top + st->viewlines - ltexth;
661         }
662         
663         ltexth += blank_lines;
664
665         barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0;
666         pix_bardiff = 0;
667         if(barheight < 20) {
668                 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */      
669                 barheight = 20;
670         }
671         barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0;
672
673         st->txtbar= *scroll;
674         st->txtbar.ymax -= barstart;
675         st->txtbar.ymin = st->txtbar.ymax - barheight;
676
677         CLAMP(st->txtbar.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
678         CLAMP(st->txtbar.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
679
680         st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
681         if(st->pix_per_line<.1) st->pix_per_line=.1f;
682
683         lhlstart = MIN2(txt_get_span(st->text->lines.first, st->text->curl), 
684                                 txt_get_span(st->text->lines.first, st->text->sell));
685         lhlend = MAX2(txt_get_span(st->text->lines.first, st->text->curl), 
686                                 txt_get_span(st->text->lines.first, st->text->sell));
687
688         if(ltexth > 0) {
689                 hlstart = (lhlstart * pix_available)/ltexth;
690                 hlend = (lhlend * pix_available)/ltexth;
691
692                 /* the scrollbar is non-linear sized */
693                 if(pix_bardiff > 0) {
694                         /* the start of the highlight is in the current viewport */
695                         if(ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) { 
696                                 /* speed the progresion of the start of the highlight through the scrollbar */
697                                 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);       
698                         }
699                         else if(lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
700                                 /* push hl start down */
701                                 hlstart = barstart + barheight;
702                         }
703                         else if(lhlend > st->top  && lhlstart < st->top && hlstart > barstart) {
704                                 /*fill out start */
705                                 hlstart = barstart;
706                         }
707
708                         if(hlend <= hlstart) { 
709                                 hlend = hlstart + 2;
710                         }
711
712                         /* the end of the highlight is in the current viewport */
713                         if(ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) { 
714                                 /* speed the progresion of the end of the highlight through the scrollbar */
715                                 hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines);    
716                         }
717                         else if(lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
718                                 /* push hl end up */
719                                 hlend = barstart;
720                         }                                       
721                         else if(lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
722                                 /* fill out end */
723                                 hlend = barstart + barheight;
724                         }
725
726                         if(hlend <= hlstart) { 
727                                 hlstart = hlend - 2;
728                         }       
729                 }       
730         }
731         else {
732                 hlstart = 0;
733                 hlend = 0;
734         }
735
736         if(hlend - hlstart < 2) { 
737                 hlend = hlstart + 2;
738         }
739         
740         st->txtscroll= *scroll;
741         st->txtscroll.ymax= ar->winy - pix_top_margin - hlstart;
742         st->txtscroll.ymin= ar->winy - pix_top_margin - hlend;
743
744         CLAMP(st->txtscroll.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
745         CLAMP(st->txtscroll.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
746 }
747
748 static void draw_textscroll(SpaceText *st, ARegion *ar, rcti *scroll)
749 {
750         bTheme *btheme= U.themes.first;
751         uiWidgetColors wcol= btheme->tui.wcol_scroll;
752         char col[3];
753         float rad;
754         
755 //      UI_ThemeColorShade(TH_SHADE1, -20);
756 //      glRecti(2, 2, 20, ar->winy-6);
757 //      uiEmboss(2, 2, 20, ar->winy-6, 1);
758
759 //      UI_ThemeColor(TH_SHADE1);
760 //      glRecti(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax);
761
762 //      uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
763         
764         uiWidgetScrollDraw(&wcol, scroll, &st->txtbar, (st->flags & ST_SCROLL_SELECT)?UI_SCROLL_PRESSED:0);
765
766         uiSetRoundBox(15);
767         rad= 0.4f*MIN2(st->txtscroll.xmax - st->txtscroll.xmin, st->txtscroll.ymax - st->txtscroll.ymin);
768         UI_GetThemeColor3ubv(TH_HILITE, col);
769         glColor4ub(col[0], col[1], col[2], 48);
770         glEnable(GL_BLEND);
771         uiRoundBox(st->txtscroll.xmin+1, st->txtscroll.ymin, st->txtscroll.xmax-1, st->txtscroll.ymax, rad);
772         glDisable(GL_BLEND);
773 }
774
775 /************************** draw markers **************************/
776
777 static void draw_markers(SpaceText *st, ARegion *ar)
778 {
779         Text *text= st->text;
780         TextMarker *marker, *next;
781         TextLine *top, *bottom, *line;
782         int offl, offc, i, cy, x1, x2, y1, y2, x, y;
783
784         for(i=st->top, top= text->lines.first; top->next && i>0; i--)
785                 top= top->next;
786
787         for(i=st->viewlines-1, bottom=top; bottom->next && i>0; i--)
788                 bottom= bottom->next;
789         
790         for(marker= text->markers.first; marker; marker= next) {
791                 next= marker->next;
792
793                 for(cy= 0, line= top; line; cy++, line= line->next) {
794                         if(cy+st->top==marker->lineno) {
795                                 /* Remove broken markers */
796                                 if(marker->end>line->len || marker->start>marker->end) {
797                                         BLI_freelinkN(&text->markers, marker);
798                                         break;
799                                 }
800
801                                 wrap_offset(st, ar, line, marker->start, &offl, &offc);
802                                 x1= get_char_pos(st, line->line, marker->start) - st->left + offc;
803                                 y1= cy + offl;
804                                 wrap_offset(st, ar, line, marker->end, &offl, &offc);
805                                 x2= get_char_pos(st, line->line, marker->end) - st->left + offc;
806                                 y2= cy + offl;
807
808                                 glColor3ub(marker->color[0], marker->color[1], marker->color[2]);
809                                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
810                                 y= ar->winy-3;
811
812                                 if(y1==y2) {
813                                         y -= y1*st->lheight;
814                                         glBegin(GL_LINE_LOOP);
815                                         glVertex2i(x+x2*st->cwidth+1, y);
816                                         glVertex2i(x+x1*st->cwidth-2, y);
817                                         glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
818                                         glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
819                                         glEnd();
820                                 }
821                                 else {
822                                         y -= y1*st->lheight;
823                                         glBegin(GL_LINE_STRIP);
824                                         glVertex2i(ar->winx, y);
825                                         glVertex2i(x+x1*st->cwidth-2, y);
826                                         glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
827                                         glVertex2i(ar->winx, y-st->lheight);
828                                         glEnd();
829                                         y-=st->lheight;
830
831                                         for(i=y1+1; i<y2; i++) {
832                                                 glBegin(GL_LINES);
833                                                 glVertex2i(x, y);
834                                                 glVertex2i(ar->winx, y);
835                                                 glVertex2i(x, y-st->lheight);
836                                                 glVertex2i(ar->winx, y-st->lheight);
837                                                 glEnd();
838                                                 y-=st->lheight;
839                                         }
840
841                                         glBegin(GL_LINE_STRIP);
842                                         glVertex2i(x, y);
843                                         glVertex2i(x+x2*st->cwidth+1, y);
844                                         glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
845                                         glVertex2i(x, y-st->lheight);
846                                         glEnd();
847                                 }
848
849                                 break;
850                         }
851
852                         if(line==bottom) break;
853                 }
854         }
855 }
856
857 /*********************** draw documentation *******************************/
858
859 static void draw_documentation(SpaceText *st, ARegion *ar)
860 {
861         TextLine *tmp;
862         char *docs, buf[DOC_WIDTH+1], *p;
863         int len, i, br, lines;
864         int boxw, boxh, l, x, y, top;
865         
866         if(!st || !st->text) return;
867         if(!texttool_text_is_active(st->text)) return;
868         
869         docs = texttool_docs_get();
870
871         if(!docs) return;
872
873         /* Count the visible lines to the cursor */
874         for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
875         if(l<0) return;
876         
877         if(st->showlinenrs) {
878                 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
879         }
880         else {
881                 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4;
882         }
883         if(texttool_suggest_first()) {
884                 x += SUGG_LIST_WIDTH*st->cwidth + 50;
885         }
886
887         top= y= ar->winy - st->lheight*l - 2;
888         len= strlen(docs);
889         boxw= DOC_WIDTH*st->cwidth + 20;
890         boxh= (DOC_HEIGHT+1)*st->lheight;
891
892         /* Draw panel */
893         UI_ThemeColor(TH_BACK);
894         glRecti(x, y, x+boxw, y-boxh);
895         UI_ThemeColor(TH_SHADE1);
896         glBegin(GL_LINE_LOOP);
897         glVertex2i(x, y);
898         glVertex2i(x+boxw, y);
899         glVertex2i(x+boxw, y-boxh);
900         glVertex2i(x, y-boxh);
901         glEnd();
902         glBegin(GL_LINE_LOOP);
903         glVertex2i(x+boxw-10, y-7);
904         glVertex2i(x+boxw-4, y-7);
905         glVertex2i(x+boxw-7, y-2);
906         glEnd();
907         glBegin(GL_LINE_LOOP);
908         glVertex2i(x+boxw-10, y-boxh+7);
909         glVertex2i(x+boxw-4, y-boxh+7);
910         glVertex2i(x+boxw-7, y-boxh+2);
911         glEnd();
912         UI_ThemeColor(TH_TEXT);
913
914         i= 0; br= DOC_WIDTH; lines= 0; // XXX -doc_scroll;
915         for(p=docs; *p; p++) {
916                 if(*p == '\r' && *(++p) != '\n') *(--p)= '\n'; /* Fix line endings */
917                 if(*p == ' ' || *p == '\t')
918                         br= i;
919                 else if(*p == '\n') {
920                         buf[i]= '\0';
921                         if(lines>=0) {
922                                 y -= st->lheight;
923                                 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
924                         }
925                         i= 0; br= DOC_WIDTH; lines++;
926                 }
927                 buf[i++]= *p;
928                 if(i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
929                         buf[br]= '\0';
930                         if(lines>=0) {
931                                 y -= st->lheight;
932                                 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
933                         }
934                         p -= i-br-1; /* Rewind pointer to last break */
935                         i= 0; br= DOC_WIDTH; lines++;
936                 }
937                 if(lines >= DOC_HEIGHT) break;
938         }
939
940         if(0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) {
941                 // XXX doc_scroll--;
942                 draw_documentation(st, ar);
943         }
944 }
945
946 /*********************** draw suggestion list *******************************/
947
948 static void draw_suggestion_list(SpaceText *st, ARegion *ar)
949 {
950         SuggItem *item, *first, *last, *sel;
951         TextLine *tmp;
952         char str[SUGG_LIST_WIDTH+1];
953         int w, boxw=0, boxh, i, l, x, y, b, *top;
954         
955         if(!st || !st->text) return;
956         if(!texttool_text_is_active(st->text)) return;
957
958         first = texttool_suggest_first();
959         last = texttool_suggest_last();
960
961         if(!first || !last) return;
962
963         text_pop_suggest_list();
964         sel = texttool_suggest_selected();
965         top = texttool_suggest_top();
966
967         /* Count the visible lines to the cursor */
968         for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
969         if(l<0) return;
970         
971         if(st->showlinenrs) {
972                 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
973         }
974         else {
975                 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4;
976         }
977         y = ar->winy - st->lheight*l - 2;
978
979         boxw = SUGG_LIST_WIDTH*st->cwidth + 20;
980         boxh = SUGG_LIST_SIZE*st->lheight + 8;
981         
982         UI_ThemeColor(TH_SHADE1);
983         glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
984         UI_ThemeColor(TH_BACK);
985         glRecti(x, y, x+boxw, y-boxh);
986
987         /* Set the top 'item' of the visible list */
988         for(i=0, item=first; i<*top && item->next; i++, item=item->next);
989
990         for(i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) {
991
992                 y -= st->lheight;
993
994                 strncpy(str, item->name, SUGG_LIST_WIDTH);
995                 str[SUGG_LIST_WIDTH] = '\0';
996
997                 w = text_font_width(st, str);
998                 
999                 if(item == sel) {
1000                         UI_ThemeColor(TH_SHADE2);
1001                         glRecti(x+16, y-3, x+16+w, y+st->lheight-3);
1002                 }
1003                 b=1; /* b=1 colour block, text is default. b=0 no block, colour text */
1004                 switch (item->type) {
1005                         case 'k': UI_ThemeColor(TH_SYNTAX_B); b=0; break;
1006                         case 'm': UI_ThemeColor(TH_TEXT); break;
1007                         case 'f': UI_ThemeColor(TH_SYNTAX_L); break;
1008                         case 'v': UI_ThemeColor(TH_SYNTAX_N); break;
1009                         case '?': UI_ThemeColor(TH_TEXT); b=0; break;
1010                 }
1011                 if(b) {
1012                         glRecti(x+8, y+2, x+11, y+5);
1013                         UI_ThemeColor(TH_TEXT);
1014                 }
1015                 text_draw(st, str, 0, 0, 1, x+16, y-1, NULL);
1016
1017                 if(item == last) break;
1018         }
1019 }
1020
1021 /*********************** draw cursor ************************/
1022
1023 static void draw_cursor(SpaceText *st, ARegion *ar)
1024 {
1025         Text *text= st->text;
1026         int vcurl, vcurc, vsell, vselc, hidden=0;
1027         int offl, offc, x, y, w, i;
1028         
1029         /* Draw the selection */
1030         if(text->curl!=text->sell || text->curc!=text->selc) {
1031                 /* Convert all to view space character coordinates */
1032                 wrap_offset(st, ar, text->curl, text->curc, &offl, &offc);
1033                 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
1034                 vcurc = get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
1035                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1036                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1037                 vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1038
1039                 if(vcurc<0) vcurc=0;
1040                 if(vselc<0) vselc=0, hidden=1;
1041                 
1042                 UI_ThemeColor(TH_SHADE2);
1043                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1044                 y= ar->winy-2;
1045
1046                 if(vcurl==vsell) {
1047                         y -= vcurl*st->lheight;
1048                         if(vcurc < vselc)
1049                                 glRecti(x+vcurc*st->cwidth-1, y, x+vselc*st->cwidth, y-st->lheight);
1050                         else
1051                                 glRecti(x+vselc*st->cwidth-1, y, x+vcurc*st->cwidth, y-st->lheight);
1052                 }
1053                 else {
1054                         int froml, fromc, tol, toc;
1055
1056                         if(vcurl < vsell) {
1057                                 froml= vcurl; tol= vsell;
1058                                 fromc= vcurc; toc= vselc;
1059                         }
1060                         else {
1061                                 froml= vsell; tol= vcurl;
1062                                 fromc= vselc; toc= vcurc;
1063                         }
1064
1065                         y -= froml*st->lheight;
1066                         glRecti(x+fromc*st->cwidth-1, y, ar->winx, y-st->lheight); y-=st->lheight;
1067                         for(i=froml+1; i<tol; i++)
1068                                 glRecti(x-4, y, ar->winx, y-st->lheight),  y-=st->lheight;
1069
1070                         glRecti(x-4, y, x+toc*st->cwidth, y-st->lheight);  y-=st->lheight;
1071                 }
1072         }
1073         else {
1074                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1075                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1076                 vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1077
1078                 if(vselc<0) {
1079                         vselc= 0;
1080                         hidden= 1;
1081                 }
1082         }
1083
1084         if(!hidden) {
1085                 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
1086                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1087                 x += vselc*st->cwidth;
1088                 y= ar->winy-2 - vsell*st->lheight;
1089                 
1090                 if(st->overwrite) {
1091                         char ch= text->sell->line[text->selc];
1092                         if(!ch) ch= ' ';
1093                         w= st->cwidth;
1094                         UI_ThemeColor(TH_HILITE);
1095                         glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1);
1096                 }
1097                 else {
1098                         UI_ThemeColor(TH_HILITE);
1099                         glRecti(x-1, y, x+1, y-st->lheight);
1100                 }
1101         }
1102 }
1103
1104 /******************* draw matching brackets *********************/
1105
1106 static void draw_brackets(SpaceText *st, ARegion *ar)
1107 {
1108         TextLine *startl, *endl, *linep;
1109         Text *text = st->text;
1110         int b, c, startc, endc, find, stack;
1111         int viewc, viewl, offl, offc, x, y;
1112         char ch;
1113
1114         if(!text->curl) return;
1115
1116         startl= text->curl;
1117         startc= text->curc;
1118         b= text_check_bracket(startl->line[startc]);
1119         if(b==0 && startc>0) b = text_check_bracket(startl->line[--startc]);
1120         if(b==0) return;
1121         
1122         linep= startl;
1123         c= startc;
1124         endl= NULL;
1125         endc= -1;
1126         find= -b;
1127         stack= 0;
1128
1129         if(b>0) {
1130                 /* opening bracket, search forward for close */
1131                 c++;
1132                 while(linep) {
1133                         while(c<linep->len) {
1134                                 b= text_check_bracket(linep->line[c]);
1135                                 if(b==find) {
1136                                         if(stack==0) {
1137                                                 endl= linep;
1138                                                 endc= c;
1139                                                 break;
1140                                         }
1141                                         stack--;
1142                                 }
1143                                 else if(b==-find) {
1144                                         stack++;
1145                                 }
1146                                 c++;
1147                         }
1148                         if(endl) break;
1149                         linep= linep->next;
1150                         c= 0;
1151                 }
1152         }
1153         else {
1154                 /* closing bracket, search backward for open */
1155                 c--;
1156                 while(linep) {
1157                         while(c>=0) {
1158                                 b= text_check_bracket(linep->line[c]);
1159                                 if(b==find) {
1160                                         if(stack==0) {
1161                                                 endl= linep;
1162                                                 endc= c;
1163                                                 break;
1164                                         }
1165                                         stack--;
1166                                 }
1167                                 else if(b==-find) {
1168                                         stack++;
1169                                 }
1170                                 c--;
1171                         }
1172                         if(endl) break;
1173                         linep= linep->prev;
1174                         if(linep) c= linep->len-1;
1175                 }
1176         }
1177
1178         if(!endl || endc==-1)
1179                 return;
1180
1181         UI_ThemeColor(TH_HILITE);       
1182         x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1183         y= ar->winy - st->lheight;
1184
1185         /* draw opening bracket */
1186         ch= startl->line[startc];
1187         wrap_offset(st, ar, startl, startc, &offl, &offc);
1188         viewc= get_char_pos(st, startl->line, startc) - st->left + offc;
1189
1190         if(viewc >= 0){
1191                 viewl= txt_get_span(text->lines.first, startl) - st->top + offl;
1192
1193                 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch);
1194                 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch);
1195         }
1196
1197         /* draw closing bracket */
1198         ch= endl->line[endc];
1199         wrap_offset(st, ar, endl, endc, &offl, &offc);
1200         viewc= get_char_pos(st, endl->line, endc) - st->left + offc;
1201
1202         if(viewc >= 0) {
1203                 viewl= txt_get_span(text->lines.first, endl) - st->top + offl;
1204
1205                 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch);
1206                 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch);
1207         }
1208 }
1209
1210 /*********************** main area drawing *************************/
1211
1212 void draw_text_main(SpaceText *st, ARegion *ar)
1213 {
1214         Text *text= st->text;
1215         TextLine *tmp;
1216         rcti scroll;
1217         char linenr[12];
1218         int i, x, y, linecount= 0;
1219
1220         /* if no text, nothing to do */
1221         if(!text)
1222                 return;
1223         
1224         /* make sure all the positional pointers exist */
1225         if(!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1226                 txt_clean_text(text);
1227         
1228         if(st->lheight) st->viewlines= (int)ar->winy/st->lheight;
1229         else st->viewlines= 0;
1230         
1231         /* update rects for scroll */
1232         calc_text_rcts(st, ar, &scroll);        /* scroll will hold the entire bar size */
1233
1234         /* update syntax formatting if needed */
1235         tmp= text->lines.first;
1236         for(i= 0; i<st->top && tmp; i++) {
1237                 if(st->showsyntax && !tmp->format)
1238                         txt_format_line(st, tmp, 0);
1239
1240                 tmp= tmp->next;
1241                 linecount++;
1242         }
1243
1244         text_font_begin(st);
1245         st->cwidth= BLF_fixed_width();
1246         st->cwidth= MAX2(st->cwidth, 1);
1247
1248         /* draw line numbers background */
1249         if(st->showlinenrs) {
1250                 st->linenrs_tot = (int)floor(log10((float)(linecount + st->viewlines))) + 1;
1251                 x= TXT_OFFSET + TEXTXLOC;
1252
1253                 UI_ThemeColor(TH_GRID);
1254                 glRecti((TXT_OFFSET-12), 0, (TXT_OFFSET-5) + TEXTXLOC, ar->winy - 2);
1255         }
1256         else {
1257                 st->linenrs_tot= 0; /* not used */
1258                 x= TXT_OFFSET;
1259         }
1260         y= ar->winy-st->lheight;
1261
1262         /* draw cursor */
1263         draw_cursor(st, ar);
1264
1265         /* draw the text */
1266         UI_ThemeColor(TH_TEXT);
1267
1268         for(i=0; y>0 && i<st->viewlines && tmp; i++, tmp= tmp->next) {
1269                 if(st->showsyntax && !tmp->format)
1270                         txt_format_line(st, tmp, 0);
1271
1272                 if(st->showlinenrs) {
1273                         /* draw line number */
1274                         if(tmp == text->curl)
1275                                 UI_ThemeColor(TH_HILITE);
1276                         else
1277                                 UI_ThemeColor(TH_TEXT);
1278
1279                         sprintf(linenr, "%d", i + linecount + 1);
1280                         /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */
1281                         text_font_draw(st, TXT_OFFSET - 7, y, linenr);
1282
1283                         UI_ThemeColor(TH_TEXT);
1284                 }
1285
1286                 if(st->wordwrap) {
1287                         /* draw word wrapped text */
1288                         int lines = text_draw_wrapped(st, tmp->line, x, y, ar->winx-x, tmp->format);
1289                         y -= lines*st->lheight;
1290                 }
1291                 else {
1292                         /* draw unwrapped text */
1293                         text_draw(st, tmp->line, st->left, 0, 1, x, y, tmp->format);
1294                         y -= st->lheight;
1295                 }
1296         }
1297         
1298         /* draw other stuff */
1299         draw_brackets(st, ar);
1300         draw_markers(st, ar);
1301         glTranslatef(0.375f, 0.375f, 0.0f); /* XXX scroll requires exact pixel space */
1302         draw_textscroll(st, ar, &scroll);
1303         draw_documentation(st, ar);
1304         draw_suggestion_list(st, ar);
1305         
1306         text_font_end(st);
1307 }
1308
1309 /************************** update ***************************/
1310
1311 void text_update_character_width(SpaceText *st)
1312 {
1313         text_font_begin(st);
1314         st->cwidth= BLF_fixed_width();
1315         st->cwidth= MAX2(st->cwidth, 1);
1316         text_font_end(st);
1317 }
1318
1319 /* Moves the view to the cursor location,
1320   also used to make sure the view isnt outside the file */
1321 void text_update_cursor_moved(SpaceText *st, ARegion *ar)
1322 {
1323         Text *text= st->text;
1324         int i, x;
1325
1326         if(!text || !text->curl) return;
1327
1328         text_update_character_width(st);
1329
1330         i= txt_get_span(text->lines.first, text->sell);
1331         if(st->top+st->viewlines <= i || st->top > i)
1332                 st->top= i - st->viewlines/2;
1333         
1334         if(st->wordwrap) {
1335                 st->left= 0;
1336         }
1337         else {
1338                 x= text_draw(st, text->sell->line, st->left, text->selc, 0, 0, 0, NULL);
1339
1340                 if(x==0 || x>ar->winx)
1341                         st->left= text->curc-0.5*(ar->winx)/st->cwidth;
1342         }
1343
1344         if(st->top < 0) st->top= 0;
1345         if(st->left <0) st->left= 0;
1346 }
1347