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