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