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