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