manual merge trunk -r 23037
[blender-staging.git] / source / blender / editors / space_text / text_draw.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * 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 /*********************** utilities ************************/
429
430 int text_check_bracket(char ch)
431 {
432         int a;
433         char opens[] = "([{";
434         char close[] = ")]}";
435         
436         for(a=0; a<3; a++) {
437                 if(ch==opens[a])
438                         return a+1;
439                 else if(ch==close[a])
440                         return -(a+1);
441         }
442         return 0;
443 }
444
445 int text_check_delim(char ch) 
446 {
447         int a;
448         char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,";
449         
450         for(a=0; a<28; a++) {
451                 if(ch==delims[a])
452                         return 1;
453         }
454         return 0;
455 }
456
457 int text_check_digit(char ch)
458 {
459         if(ch < '0') return 0;
460         if(ch <= '9') return 1;
461         return 0;
462 }
463
464 int text_check_identifier(char ch)
465 {
466         if(ch < '0') return 0;
467         if(ch <= '9') return 1;
468         if(ch < 'A') return 0;
469         if(ch <= 'Z' || ch == '_') return 1;
470         if(ch < 'a') return 0;
471         if(ch <= 'z') return 1;
472         return 0;
473 }
474
475 int text_check_whitespace(char ch)
476 {
477         if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
478                 return 1;
479         return 0;
480 }
481
482 /************************** draw text *****************************/
483
484 /***********************/ /*
485
486 Notes on word-wrap
487 --
488 All word-wrap functions follow the algorithm below to maintain consistency.
489         line            The line to wrap (tabs converted to spaces)
490         view_width      The maximum number of characters displayable in the region
491                                 This equals region_width/font_width for the region
492         wrap_chars      Characters that allow wrapping. This equals [' ', '\t', '-']
493
494 def wrap(line, view_width, wrap_chars):
495         draw_start = 0
496         draw_end = view_width
497         pos = 0
498         for c in line:
499                 if pos-draw_start >= view_width:
500                         print line[draw_start:draw_end]
501                         draw_start = draw_end
502                         draw_end += view_width
503                 elif c in wrap_chars:
504                         draw_end = pos+1
505                 pos += 1
506         print line[draw_start:]
507
508 */ /***********************/
509
510 int wrap_width(SpaceText *st, ARegion *ar)
511 {
512         int x, max;
513         
514         x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
515         max= (ar->winx-x)/st->cwidth;
516         return max>8 ? max : 8;
517 }
518
519 /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
520 void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
521 {
522         Text *text;
523         TextLine *linep;
524         int i, j, start, end, chars, max, chop;
525         char ch;
526
527         *offl= *offc= 0;
528
529         if(!st->text) return;
530         if(!st->wordwrap) return;
531
532         text= st->text;
533
534         /* Move pointer to first visible line (top) */
535         linep= text->lines.first;
536         i= st->top;
537         while(i>0 && linep) {
538                 if(linep == linein) return; /* Line before top */
539                 linep= linep->next;
540                 i--;
541         }
542
543         max= wrap_width(st, ar);
544
545         while(linep) {
546                 start= 0;
547                 end= max;
548                 chop= 1;
549                 chars= 0;
550                 *offc= 0;
551                 for(i=0, j=0; linep->line[j]!='\0'; j++) {
552
553                         /* Mimic replacement of tabs */
554                         ch= linep->line[j];
555                         if(ch=='\t') {
556                                 chars= st->tabnumber-i%st->tabnumber;
557                                 if(linep==linein && i<cursin) cursin += chars-1;
558                                 ch= ' ';
559                         }
560                         else
561                                 chars= 1;
562
563                         while(chars--) {
564                                 if(i-start>=max) {
565                                         if(chop && linep==linein && i >= cursin)
566                                                 return;
567                                         (*offl)++;
568                                         *offc -= end-start;
569                                         start= end;
570                                         end += max;
571                                         chop= 1;
572                                 }
573                                 else if(ch==' ' || ch=='-') {
574                                         end = i+1;
575                                         chop= 0;
576                                         if(linep==linein && i >= cursin)
577                                                 return;
578                                 }
579                                 i++;
580                         }
581                 }
582                 if(linep==linein) break;
583                 linep= linep->next;
584         }
585 }
586
587 static int get_char_pos(SpaceText *st, char *line, int cur)
588 {
589         int a=0, i;
590         
591         for(i=0; i<cur && line[i]; i++) {
592                 if(line[i]=='\t')
593                         a += st->tabnumber-a%st->tabnumber;
594                 else
595                         a++;
596         }
597         return a;
598 }
599
600 static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format)
601 {
602         FlattenString fs;
603         int basex, i, a, len, start, end, max, lines;
604         
605         len= flatten_string(st, &fs, str);
606         str= fs.buf;
607         max= w/st->cwidth;
608         if(max<8) max= 8;
609         basex= x;
610
611         lines= 1;
612         start= 0;
613         end= max;
614         for(i=0; i<len; i++) {
615                 if(i-start >= max) {
616                         /* Draw the visible portion of text on the overshot line */
617                         for(a=start; a<end; a++) {
618                                 if(st->showsyntax && format) format_draw_color(format[a]);
619                                 x += text_font_draw_character(st, x, y, str[a]);
620                         }
621                         y -= st->lheight;
622                         x= basex;
623                         lines++;
624                         start= end;
625                         end += max;
626                 }
627                 else if(str[i]==' ' || str[i]=='-') {
628                         end = i+1;
629                 }
630         }
631
632         /* Draw the remaining text */
633         for(a=start; a<len; a++) {
634                 if(st->showsyntax && format)
635                         format_draw_color(format[a]);
636
637                 x += text_font_draw_character(st, x, y, str[a]);
638         }
639
640         flatten_string_free(&fs);
641
642         return lines;
643 }
644
645 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format)
646 {
647         FlattenString fs;
648         int r=0, w= 0;
649         int *acc;
650         char *in;
651
652         w= flatten_string(st, &fs, str);
653         if(w < cshift) {
654                 flatten_string_free(&fs);
655                 return 0; /* String is shorter than shift */
656         }
657         
658         in= fs.buf+cshift;
659         acc= fs.accum+cshift;
660         w= w-cshift;
661
662         if(draw) {
663                 if(st->showsyntax && format) {
664                         int amount, a;
665                         format = format+cshift;
666                 
667                         amount = strlen(in);
668                         
669                         for(a = 0; a < amount; a++) {
670                                 format_draw_color(format[a]);
671                                 x += text_font_draw_character(st, x, y, in[a]);
672                         }
673                 }
674                 else
675                         text_font_draw(st, x, y, in);
676         }
677         else {
678                 while(w-- && *acc++ < maxwidth)
679                         r+= st->cwidth;
680         }
681
682         flatten_string_free(&fs);
683
684         if(cshift && r==0)
685                 return 0;
686         else if(st->showlinenrs)
687                 return r+TXT_OFFSET+TEXTXLOC;
688         else
689                 return r+TXT_OFFSET;
690 }
691
692 /************************ draw scrollbar *****************************/
693
694 static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll)
695 {
696         int lhlstart, lhlend, ltexth;
697         short barheight, barstart, hlstart, hlend, blank_lines;
698         short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
699
700         pix_top_margin = 8;
701         pix_bottom_margin = 4;
702         pix_available = ar->winy - pix_top_margin - pix_bottom_margin;
703         ltexth= txt_get_span(st->text->lines.first, st->text->lines.last);
704         blank_lines = st->viewlines / 2;
705         
706         /* nicer code: use scroll rect for entire bar */
707         scroll->xmin= 5;
708         scroll->xmax= 17;
709         scroll->ymin= 4;
710         scroll->ymax= 4+pix_available;
711         
712         /* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */
713         if(ltexth + blank_lines < st->top + st->viewlines) {
714                 blank_lines = st->top + st->viewlines - ltexth;
715         }
716         
717         ltexth += blank_lines;
718
719         barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0;
720         pix_bardiff = 0;
721         if(barheight < 20) {
722                 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */      
723                 barheight = 20;
724         }
725         barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0;
726
727         st->txtbar= *scroll;
728         st->txtbar.ymax -= barstart;
729         st->txtbar.ymin = st->txtbar.ymax - barheight;
730
731         CLAMP(st->txtbar.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
732         CLAMP(st->txtbar.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
733
734         st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
735         if(st->pix_per_line<.1) st->pix_per_line=.1f;
736
737         lhlstart = MIN2(txt_get_span(st->text->lines.first, st->text->curl), 
738                                 txt_get_span(st->text->lines.first, st->text->sell));
739         lhlend = MAX2(txt_get_span(st->text->lines.first, st->text->curl), 
740                                 txt_get_span(st->text->lines.first, st->text->sell));
741
742         if(ltexth > 0) {
743                 hlstart = (lhlstart * pix_available)/ltexth;
744                 hlend = (lhlend * pix_available)/ltexth;
745
746                 /* the scrollbar is non-linear sized */
747                 if(pix_bardiff > 0) {
748                         /* the start of the highlight is in the current viewport */
749                         if(ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) { 
750                                 /* speed the progresion of the start of the highlight through the scrollbar */
751                                 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);       
752                         }
753                         else if(lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
754                                 /* push hl start down */
755                                 hlstart = barstart + barheight;
756                         }
757                         else if(lhlend > st->top  && lhlstart < st->top && hlstart > barstart) {
758                                 /*fill out start */
759                                 hlstart = barstart;
760                         }
761
762                         if(hlend <= hlstart) { 
763                                 hlend = hlstart + 2;
764                         }
765
766                         /* the end of the highlight is in the current viewport */
767                         if(ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) { 
768                                 /* speed the progresion of the end of the highlight through the scrollbar */
769                                 hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines);    
770                         }
771                         else if(lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
772                                 /* push hl end up */
773                                 hlend = barstart;
774                         }                                       
775                         else if(lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
776                                 /* fill out end */
777                                 hlend = barstart + barheight;
778                         }
779
780                         if(hlend <= hlstart) { 
781                                 hlstart = hlend - 2;
782                         }       
783                 }       
784         }
785         else {
786                 hlstart = 0;
787                 hlend = 0;
788         }
789
790         if(hlend - hlstart < 2) { 
791                 hlend = hlstart + 2;
792         }
793         
794         st->txtscroll= *scroll;
795         st->txtscroll.ymax= ar->winy - pix_top_margin - hlstart;
796         st->txtscroll.ymin= ar->winy - pix_top_margin - hlend;
797
798         CLAMP(st->txtscroll.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
799         CLAMP(st->txtscroll.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
800 }
801
802 static void draw_textscroll(SpaceText *st, ARegion *ar, rcti *scroll)
803 {
804         bTheme *btheme= U.themes.first;
805         uiWidgetColors wcol= btheme->tui.wcol_scroll;
806         char col[3];
807         float rad;
808         
809 //      UI_ThemeColorShade(TH_SHADE1, -20);
810 //      glRecti(2, 2, 20, ar->winy-6);
811 //      uiEmboss(2, 2, 20, ar->winy-6, 1);
812
813 //      UI_ThemeColor(TH_SHADE1);
814 //      glRecti(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax);
815
816 //      uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
817         
818         uiWidgetScrollDraw(&wcol, scroll, &st->txtbar, (st->flags & ST_SCROLL_SELECT)?UI_SCROLL_PRESSED:0);
819
820         uiSetRoundBox(15);
821         rad= 0.4f*MIN2(st->txtscroll.xmax - st->txtscroll.xmin, st->txtscroll.ymax - st->txtscroll.ymin);
822         UI_GetThemeColor3ubv(TH_HILITE, col);
823         glColor4ub(col[0], col[1], col[2], 48);
824         glEnable(GL_BLEND);
825         uiRoundBox(st->txtscroll.xmin+1, st->txtscroll.ymin, st->txtscroll.xmax-1, st->txtscroll.ymax, rad);
826         glDisable(GL_BLEND);
827 }
828
829 /************************** draw markers **************************/
830
831 static void draw_markers(SpaceText *st, ARegion *ar)
832 {
833         Text *text= st->text;
834         TextMarker *marker, *next;
835         TextLine *top, *bottom, *line;
836         int offl, offc, i, cy, x1, x2, y1, y2, x, y;
837
838         for(i=st->top, top= text->lines.first; top->next && i>0; i--)
839                 top= top->next;
840
841         for(i=st->viewlines-1, bottom=top; bottom->next && i>0; i--)
842                 bottom= bottom->next;
843         
844         for(marker= text->markers.first; marker; marker= next) {
845                 next= marker->next;
846
847                 for(cy= 0, line= top; line; cy++, line= line->next) {
848                         if(cy+st->top==marker->lineno) {
849                                 /* Remove broken markers */
850                                 if(marker->end>line->len || marker->start>marker->end) {
851                                         BLI_freelinkN(&text->markers, marker);
852                                         break;
853                                 }
854
855                                 wrap_offset(st, ar, line, marker->start, &offl, &offc);
856                                 x1= get_char_pos(st, line->line, marker->start) - st->left + offc;
857                                 y1= cy + offl;
858                                 wrap_offset(st, ar, line, marker->end, &offl, &offc);
859                                 x2= get_char_pos(st, line->line, marker->end) - st->left + offc;
860                                 y2= cy + offl;
861
862                                 glColor3ub(marker->color[0], marker->color[1], marker->color[2]);
863                                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
864                                 y= ar->winy-3;
865
866                                 if(y1==y2) {
867                                         y -= y1*st->lheight;
868                                         glBegin(GL_LINE_LOOP);
869                                         glVertex2i(x+x2*st->cwidth+1, y);
870                                         glVertex2i(x+x1*st->cwidth-2, y);
871                                         glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
872                                         glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
873                                         glEnd();
874                                 }
875                                 else {
876                                         y -= y1*st->lheight;
877                                         glBegin(GL_LINE_STRIP);
878                                         glVertex2i(ar->winx, y);
879                                         glVertex2i(x+x1*st->cwidth-2, y);
880                                         glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
881                                         glVertex2i(ar->winx, y-st->lheight);
882                                         glEnd();
883                                         y-=st->lheight;
884
885                                         for(i=y1+1; i<y2; i++) {
886                                                 glBegin(GL_LINES);
887                                                 glVertex2i(x, y);
888                                                 glVertex2i(ar->winx, y);
889                                                 glVertex2i(x, y-st->lheight);
890                                                 glVertex2i(ar->winx, y-st->lheight);
891                                                 glEnd();
892                                                 y-=st->lheight;
893                                         }
894
895                                         glBegin(GL_LINE_STRIP);
896                                         glVertex2i(x, y);
897                                         glVertex2i(x+x2*st->cwidth+1, y);
898                                         glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
899                                         glVertex2i(x, y-st->lheight);
900                                         glEnd();
901                                 }
902
903                                 break;
904                         }
905
906                         if(line==bottom) break;
907                 }
908         }
909 }
910
911 /*********************** draw documentation *******************************/
912
913 static void draw_documentation(SpaceText *st, ARegion *ar)
914 {
915         TextLine *tmp;
916         char *docs, buf[DOC_WIDTH+1], *p;
917         int len, i, br, lines;
918         int boxw, boxh, l, x, y, top;
919         
920         if(!st || !st->text) return;
921         if(!texttool_text_is_active(st->text)) return;
922         
923         docs = texttool_docs_get();
924
925         if(!docs) return;
926
927         /* Count the visible lines to the cursor */
928         for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
929         if(l<0) return;
930         
931         if(st->showlinenrs) {
932                 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
933         }
934         else {
935                 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4;
936         }
937         if(texttool_suggest_first()) {
938                 x += SUGG_LIST_WIDTH*st->cwidth + 50;
939         }
940
941         top= y= ar->winy - st->lheight*l - 2;
942         len= strlen(docs);
943         boxw= DOC_WIDTH*st->cwidth + 20;
944         boxh= (DOC_HEIGHT+1)*st->lheight;
945
946         /* Draw panel */
947         UI_ThemeColor(TH_BACK);
948         glRecti(x, y, x+boxw, y-boxh);
949         UI_ThemeColor(TH_SHADE1);
950         glBegin(GL_LINE_LOOP);
951         glVertex2i(x, y);
952         glVertex2i(x+boxw, y);
953         glVertex2i(x+boxw, y-boxh);
954         glVertex2i(x, y-boxh);
955         glEnd();
956         glBegin(GL_LINE_LOOP);
957         glVertex2i(x+boxw-10, y-7);
958         glVertex2i(x+boxw-4, y-7);
959         glVertex2i(x+boxw-7, y-2);
960         glEnd();
961         glBegin(GL_LINE_LOOP);
962         glVertex2i(x+boxw-10, y-boxh+7);
963         glVertex2i(x+boxw-4, y-boxh+7);
964         glVertex2i(x+boxw-7, y-boxh+2);
965         glEnd();
966         UI_ThemeColor(TH_TEXT);
967
968         i= 0; br= DOC_WIDTH; lines= 0; // XXX -doc_scroll;
969         for(p=docs; *p; p++) {
970                 if(*p == '\r' && *(++p) != '\n') *(--p)= '\n'; /* Fix line endings */
971                 if(*p == ' ' || *p == '\t')
972                         br= i;
973                 else if(*p == '\n') {
974                         buf[i]= '\0';
975                         if(lines>=0) {
976                                 y -= st->lheight;
977                                 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
978                         }
979                         i= 0; br= DOC_WIDTH; lines++;
980                 }
981                 buf[i++]= *p;
982                 if(i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
983                         buf[br]= '\0';
984                         if(lines>=0) {
985                                 y -= st->lheight;
986                                 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
987                         }
988                         p -= i-br-1; /* Rewind pointer to last break */
989                         i= 0; br= DOC_WIDTH; lines++;
990                 }
991                 if(lines >= DOC_HEIGHT) break;
992         }
993
994         if(0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) {
995                 // XXX doc_scroll--;
996                 draw_documentation(st, ar);
997         }
998 }
999
1000 /*********************** draw suggestion list *******************************/
1001
1002 static void draw_suggestion_list(SpaceText *st, ARegion *ar)
1003 {
1004         SuggItem *item, *first, *last, *sel;
1005         TextLine *tmp;
1006         char str[SUGG_LIST_WIDTH+1];
1007         int w, boxw=0, boxh, i, l, x, y, b, *top;
1008         
1009         if(!st || !st->text) return;
1010         if(!texttool_text_is_active(st->text)) return;
1011
1012         first = texttool_suggest_first();
1013         last = texttool_suggest_last();
1014
1015         if(!first || !last) return;
1016
1017         text_pop_suggest_list();
1018         sel = texttool_suggest_selected();
1019         top = texttool_suggest_top();
1020
1021         /* Count the visible lines to the cursor */
1022         for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1023         if(l<0) return;
1024         
1025         if(st->showlinenrs) {
1026                 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1027         }
1028         else {
1029                 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4;
1030         }
1031         y = ar->winy - st->lheight*l - 2;
1032
1033         boxw = SUGG_LIST_WIDTH*st->cwidth + 20;
1034         boxh = SUGG_LIST_SIZE*st->lheight + 8;
1035         
1036         UI_ThemeColor(TH_SHADE1);
1037         glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
1038         UI_ThemeColor(TH_BACK);
1039         glRecti(x, y, x+boxw, y-boxh);
1040
1041         /* Set the top 'item' of the visible list */
1042         for(i=0, item=first; i<*top && item->next; i++, item=item->next);
1043
1044         for(i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) {
1045
1046                 y -= st->lheight;
1047
1048                 strncpy(str, item->name, SUGG_LIST_WIDTH);
1049                 str[SUGG_LIST_WIDTH] = '\0';
1050
1051                 w = text_font_width(st, str);
1052                 
1053                 if(item == sel) {
1054                         UI_ThemeColor(TH_SHADE2);
1055                         glRecti(x+16, y-3, x+16+w, y+st->lheight-3);
1056                 }
1057                 b=1; /* b=1 colour block, text is default. b=0 no block, colour text */
1058                 switch (item->type) {
1059                         case 'k': UI_ThemeColor(TH_SYNTAX_B); b=0; break;
1060                         case 'm': UI_ThemeColor(TH_TEXT); break;
1061                         case 'f': UI_ThemeColor(TH_SYNTAX_L); break;
1062                         case 'v': UI_ThemeColor(TH_SYNTAX_N); break;
1063                         case '?': UI_ThemeColor(TH_TEXT); b=0; break;
1064                 }
1065                 if(b) {
1066                         glRecti(x+8, y+2, x+11, y+5);
1067                         UI_ThemeColor(TH_TEXT);
1068                 }
1069                 text_draw(st, str, 0, 0, 1, x+16, y-1, NULL);
1070
1071                 if(item == last) break;
1072         }
1073 }
1074
1075 /*********************** draw cursor ************************/
1076
1077 static void draw_cursor(SpaceText *st, ARegion *ar)
1078 {
1079         Text *text= st->text;
1080         int vcurl, vcurc, vsell, vselc, hidden=0;
1081         int offl, offc, x, y, w, i;
1082         
1083         /* Draw the selection */
1084         if(text->curl!=text->sell || text->curc!=text->selc) {
1085                 /* Convert all to view space character coordinates */
1086                 wrap_offset(st, ar, text->curl, text->curc, &offl, &offc);
1087                 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
1088                 vcurc = get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
1089                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1090                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1091                 vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1092
1093                 if(vcurc<0) vcurc=0;
1094                 if(vselc<0) vselc=0, hidden=1;
1095                 
1096                 UI_ThemeColor(TH_SHADE2);
1097                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1098                 y= ar->winy-2;
1099
1100                 if(vcurl==vsell) {
1101                         y -= vcurl*st->lheight;
1102                         if(vcurc < vselc)
1103                                 glRecti(x+vcurc*st->cwidth-1, y, x+vselc*st->cwidth, y-st->lheight);
1104                         else
1105                                 glRecti(x+vselc*st->cwidth-1, y, x+vcurc*st->cwidth, y-st->lheight);
1106                 }
1107                 else {
1108                         int froml, fromc, tol, toc;
1109
1110                         if(vcurl < vsell) {
1111                                 froml= vcurl; tol= vsell;
1112                                 fromc= vcurc; toc= vselc;
1113                         }
1114                         else {
1115                                 froml= vsell; tol= vcurl;
1116                                 fromc= vselc; toc= vcurc;
1117                         }
1118
1119                         y -= froml*st->lheight;
1120                         glRecti(x+fromc*st->cwidth-1, y, ar->winx, y-st->lheight); y-=st->lheight;
1121                         for(i=froml+1; i<tol; i++)
1122                                 glRecti(x-4, y, ar->winx, y-st->lheight),  y-=st->lheight;
1123
1124                         glRecti(x-4, y, x+toc*st->cwidth, y-st->lheight);  y-=st->lheight;
1125                 }
1126         }
1127         else {
1128                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1129                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1130                 vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1131
1132                 if(vselc<0) {
1133                         vselc= 0;
1134                         hidden= 1;
1135                 }
1136         }
1137
1138         if(!hidden) {
1139                 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
1140                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1141                 x += vselc*st->cwidth;
1142                 y= ar->winy-2 - vsell*st->lheight;
1143                 
1144                 if(st->overwrite) {
1145                         char ch= text->sell->line[text->selc];
1146                         if(!ch) ch= ' ';
1147                         w= st->cwidth;
1148                         UI_ThemeColor(TH_HILITE);
1149                         glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1);
1150                 }
1151                 else {
1152                         UI_ThemeColor(TH_HILITE);
1153                         glRecti(x-1, y, x+1, y-st->lheight);
1154                 }
1155         }
1156 }
1157
1158 /******************* draw matching brackets *********************/
1159
1160 static void draw_brackets(SpaceText *st, ARegion *ar)
1161 {
1162         TextLine *startl, *endl, *linep;
1163         Text *text = st->text;
1164         int b, c, startc, endc, find, stack;
1165         int viewc, viewl, offl, offc, x, y;
1166         char ch;
1167
1168         if(!text->curl) return;
1169
1170         startl= text->curl;
1171         startc= text->curc;
1172         b= text_check_bracket(startl->line[startc]);
1173         if(b==0 && startc>0) b = text_check_bracket(startl->line[--startc]);
1174         if(b==0) return;
1175         
1176         linep= startl;
1177         c= startc;
1178         endl= NULL;
1179         endc= -1;
1180         find= -b;
1181         stack= 0;
1182
1183         if(b>0) {
1184                 /* opening bracket, search forward for close */
1185                 c++;
1186                 while(linep) {
1187                         while(c<linep->len) {
1188                                 b= text_check_bracket(linep->line[c]);
1189                                 if(b==find) {
1190                                         if(stack==0) {
1191                                                 endl= linep;
1192                                                 endc= c;
1193                                                 break;
1194                                         }
1195                                         stack--;
1196                                 }
1197                                 else if(b==-find) {
1198                                         stack++;
1199                                 }
1200                                 c++;
1201                         }
1202                         if(endl) break;
1203                         linep= linep->next;
1204                         c= 0;
1205                 }
1206         }
1207         else {
1208                 /* closing bracket, search backward for open */
1209                 c--;
1210                 while(linep) {
1211                         while(c>=0) {
1212                                 b= text_check_bracket(linep->line[c]);
1213                                 if(b==find) {
1214                                         if(stack==0) {
1215                                                 endl= linep;
1216                                                 endc= c;
1217                                                 break;
1218                                         }
1219                                         stack--;
1220                                 }
1221                                 else if(b==-find) {
1222                                         stack++;
1223                                 }
1224                                 c--;
1225                         }
1226                         if(endl) break;
1227                         linep= linep->prev;
1228                         if(linep) c= linep->len-1;
1229                 }
1230         }
1231
1232         if(!endl || endc==-1)
1233                 return;
1234
1235         UI_ThemeColor(TH_HILITE);       
1236         x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1237         y= ar->winy - st->lheight;
1238
1239         /* draw opening bracket */
1240         ch= startl->line[startc];
1241         wrap_offset(st, ar, startl, startc, &offl, &offc);
1242         viewc= get_char_pos(st, startl->line, startc) - st->left + offc;
1243
1244         if(viewc >= 0){
1245                 viewl= txt_get_span(text->lines.first, startl) - st->top + offl;
1246
1247                 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch);
1248                 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch);
1249         }
1250
1251         /* draw closing bracket */
1252         ch= endl->line[endc];
1253         wrap_offset(st, ar, endl, endc, &offl, &offc);
1254         viewc= get_char_pos(st, endl->line, endc) - st->left + offc;
1255
1256         if(viewc >= 0) {
1257                 viewl= txt_get_span(text->lines.first, endl) - st->top + offl;
1258
1259                 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch);
1260                 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch);
1261         }
1262 }
1263
1264 /*********************** main area drawing *************************/
1265
1266 void draw_text_main(SpaceText *st, ARegion *ar)
1267 {
1268         Text *text= st->text;
1269         TextLine *tmp;
1270         rcti scroll;
1271         char linenr[12];
1272         int i, x, y, linecount= 0;
1273
1274         /* if no text, nothing to do */
1275         if(!text)
1276                 return;
1277         
1278         /* make sure all the positional pointers exist */
1279         if(!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1280                 txt_clean_text(text);
1281         
1282         if(st->lheight) st->viewlines= (int)ar->winy/st->lheight;
1283         else st->viewlines= 0;
1284         
1285         /* update rects for scroll */
1286         calc_text_rcts(st, ar, &scroll);        /* scroll will hold the entire bar size */
1287
1288         /* update syntax formatting if needed */
1289         tmp= text->lines.first;
1290         for(i= 0; i<st->top && tmp; i++) {
1291                 if(st->showsyntax && !tmp->format)
1292                         txt_format_line(st, tmp, 0);
1293
1294                 tmp= tmp->next;
1295                 linecount++;
1296         }
1297
1298         text_font_begin(st);
1299         st->cwidth= BLF_fixed_width();
1300         st->cwidth= MAX2(st->cwidth, 1);
1301
1302         /* draw line numbers background */
1303         if(st->showlinenrs) {
1304                 st->linenrs_tot = (int)floor(log10((float)(linecount + st->viewlines))) + 1;
1305                 x= TXT_OFFSET + TEXTXLOC;
1306
1307                 UI_ThemeColor(TH_GRID);
1308                 glRecti((TXT_OFFSET-12), 0, (TXT_OFFSET-5) + TEXTXLOC, ar->winy - 2);
1309         }
1310         else {
1311                 st->linenrs_tot= 0; /* not used */
1312                 x= TXT_OFFSET;
1313         }
1314         y= ar->winy-st->lheight;
1315
1316         /* draw cursor */
1317         draw_cursor(st, ar);
1318
1319         /* draw the text */
1320         UI_ThemeColor(TH_TEXT);
1321
1322         for(i=0; y>0 && i<st->viewlines && tmp; i++, tmp= tmp->next) {
1323                 if(st->showsyntax && !tmp->format)
1324                         txt_format_line(st, tmp, 0);
1325
1326                 if(st->showlinenrs) {
1327                         /* draw line number */
1328                         if(tmp == text->curl)
1329                                 UI_ThemeColor(TH_HILITE);
1330                         else
1331                                 UI_ThemeColor(TH_TEXT);
1332
1333                         sprintf(linenr, "%d", i + linecount + 1);
1334                         /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */
1335                         text_font_draw(st, TXT_OFFSET - 7, y, linenr);
1336
1337                         UI_ThemeColor(TH_TEXT);
1338                 }
1339
1340                 if(st->wordwrap) {
1341                         /* draw word wrapped text */
1342                         int lines = text_draw_wrapped(st, tmp->line, x, y, ar->winx-x, tmp->format);
1343                         y -= lines*st->lheight;
1344                 }
1345                 else {
1346                         /* draw unwrapped text */
1347                         text_draw(st, tmp->line, st->left, 0, 1, x, y, tmp->format);
1348                         y -= st->lheight;
1349                 }
1350         }
1351         
1352         /* draw other stuff */
1353         draw_brackets(st, ar);
1354         draw_markers(st, ar);
1355         glTranslatef(0.375f, 0.375f, 0.0f); /* XXX scroll requires exact pixel space */
1356         draw_textscroll(st, ar, &scroll);
1357         draw_documentation(st, ar);
1358         draw_suggestion_list(st, ar);
1359         
1360         text_font_end(st);
1361 }
1362
1363 /************************** update ***************************/
1364
1365 void text_update_character_width(SpaceText *st)
1366 {
1367         text_font_begin(st);
1368         st->cwidth= BLF_fixed_width();
1369         st->cwidth= MAX2(st->cwidth, 1);
1370         text_font_end(st);
1371 }
1372
1373 /* Moves the view to the cursor location,
1374   also used to make sure the view isnt outside the file */
1375 void text_update_cursor_moved(SpaceText *st, ARegion *ar)
1376 {
1377         Text *text= st->text;
1378         int i, x;
1379
1380         if(!text || !text->curl) return;
1381
1382         text_update_character_width(st);
1383
1384         i= txt_get_span(text->lines.first, text->sell);
1385         if(st->top+st->viewlines <= i || st->top > i)
1386                 st->top= i - st->viewlines/2;
1387         
1388         if(st->wordwrap) {
1389                 st->left= 0;
1390         }
1391         else {
1392                 x= text_draw(st, text->sell->line, st->left, text->selc, 0, 0, 0, NULL);
1393
1394                 if(x==0 || x>ar->winx)
1395                         st->left= text->curc-0.5*(ar->winx)/st->cwidth;
1396         }
1397
1398         if(st->top < 0) st->top= 0;
1399         if(st->left <0) st->left= 0;
1400 }
1401