code cleanup: favor braces when blocks have mixed brace use.
[blender.git] / source / blender / editors / space_text / text_draw.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16  *
17  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
18  * All rights reserved.
19  *
20  * The Original Code is: all of this file.
21  *
22  * Contributor(s): none yet.
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_text/text_draw.c
28  *  \ingroup sptext
29  */
30
31
32
33 #include "MEM_guardedalloc.h"
34
35 #include "BLF_api.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_math.h"
39
40 #include "DNA_text_types.h"
41 #include "DNA_space_types.h"
42 #include "DNA_screen_types.h"
43
44 #include "BKE_context.h"
45 #include "BKE_suggestions.h"
46 #include "BKE_text.h"
47
48 #include "BIF_gl.h"
49
50 #include "UI_interface.h"
51 #include "UI_resources.h"
52 #include "UI_view2d.h"
53
54 #include "text_intern.h"
55 #include "text_format.h"
56
57 /******************** text font drawing ******************/
58 // XXX, fixme
59 #define mono blf_mono_font
60
61 static void text_font_begin(SpaceText *st)
62 {
63         BLF_size(mono, st->lheight_dpi, 72);
64 }
65
66 static void text_font_end(SpaceText *UNUSED(st))
67 {
68 }
69
70 static int text_font_draw(SpaceText *UNUSED(st), int x, int y, const char *str)
71 {
72         BLF_position(mono, x, y, 0);
73         BLF_draw(mono, str, BLF_DRAW_STR_DUMMY_MAX);
74
75         return BLF_width(mono, str);
76 }
77
78 static int text_font_draw_character(SpaceText *st, int x, int y, char c)
79 {
80         BLF_position(mono, x, y, 0);
81         BLF_draw(mono, &c, 1);
82
83         return st->cwidth;
84 }
85
86 static int text_font_draw_character_utf8(SpaceText *st, int x, int y, const char *c)
87 {
88         const size_t len = BLI_str_utf8_size_safe(c);
89         BLF_position(mono, x, y, 0);
90         BLF_draw(mono, c, len);
91         return st->cwidth;
92 }
93
94 #if 0
95 /* Formats every line of the current text */
96 static void txt_format_text(SpaceText *st) 
97 {
98         TextLine *linep;
99
100         if (!st->text) return;
101
102         for (linep = st->text->lines.first; linep; linep = linep->next)
103                 txt_format_line(st, linep, 0);
104 }
105 #endif
106
107 /* Sets the current drawing color based on the format character specified */
108 static void format_draw_color(char formatchar)
109 {
110         switch (formatchar) {
111                 case FMT_TYPE_WHITESPACE:
112                         break;
113                 case FMT_TYPE_SYMBOL:
114                         UI_ThemeColor(TH_SYNTAX_S);
115                         break;
116                 case FMT_TYPE_COMMENT:
117                         UI_ThemeColor(TH_SYNTAX_C);
118                         break;
119                 case FMT_TYPE_NUMERAL:
120                         UI_ThemeColor(TH_SYNTAX_N);
121                         break;
122                 case FMT_TYPE_STRING:
123                         UI_ThemeColor(TH_SYNTAX_L);
124                         break;
125                 case FMT_TYPE_DIRECTIVE:
126                         UI_ThemeColor(TH_SYNTAX_D);
127                         break;
128                 case FMT_TYPE_SPECIAL:
129                         UI_ThemeColor(TH_SYNTAX_V);
130                         break;
131                 case FMT_TYPE_RESERVED:
132                         UI_ThemeColor(TH_SYNTAX_R);
133                         break;
134                 case FMT_TYPE_KEYWORD:
135                         UI_ThemeColor(TH_SYNTAX_B);
136                         break;
137                 case FMT_TYPE_DEFAULT:
138                 default:
139                         UI_ThemeColor(TH_TEXT);
140                         break;
141         }
142 }
143
144 /************************** draw text *****************************/
145
146 /* Notes on word-wrap
147  * --
148  * All word-wrap functions follow the algorithm below to maintain consistency.
149  *     line        The line to wrap (tabs converted to spaces)
150  *     view_width    The maximum number of characters displayable in the region
151  *                 This equals region_width/font_width for the region
152  *     wrap_chars    Characters that allow wrapping. This equals [' ', '\t', '-']
153  * 
154  * def wrap(line, view_width, wrap_chars):
155  *     draw_start = 0
156  *     draw_end = view_width
157  *     pos = 0
158  *     for c in line:
159  *         if pos-draw_start >= view_width:
160  *             print line[draw_start:draw_end]
161  *             draw_start = draw_end
162  *             draw_end += view_width
163  *         elif c in wrap_chars:
164  *             draw_end = pos+1
165  *         pos += 1
166  *     print line[draw_start:]
167  * 
168  */
169
170 int wrap_width(SpaceText *st, ARegion *ar)
171 {
172         int winx = ar->winx - TXT_SCROLL_WIDTH;
173         int x, max;
174         
175         x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
176         max = st->cwidth ? (winx - x) / st->cwidth : 0;
177         return max > 8 ? max : 8;
178 }
179
180 /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
181 void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
182 {
183         Text *text;
184         TextLine *linep;
185         int i, j, start, end, max, chop;
186         char ch;
187
188         *offl = *offc = 0;
189
190         if (!st->text) return;
191         if (!st->wordwrap) return;
192
193         text = st->text;
194
195         /* Move pointer to first visible line (top) */
196         linep = text->lines.first;
197         i = st->top;
198         while (i > 0 && linep) {
199                 int lines = text_get_visible_lines(st, ar, linep->line);
200
201                 /* Line before top */
202                 if (linep == linein) {
203                         if (lines <= i)
204                                 /* no visible part of line */
205                                 return;
206                 }
207
208                 if (i - lines < 0) {
209                         break;
210                 }
211                 else {
212                         linep = linep->next;
213                         (*offl) += lines - 1;
214                         i -= lines;
215                 }
216         }
217
218         max = wrap_width(st, ar);
219         cursin = txt_utf8_offset_to_index(linein->line, cursin);
220
221         while (linep) {
222                 start = 0;
223                 end = max;
224                 chop = 1;
225                 *offc = 0;
226                 for (i = 0, j = 0; linep->line[j]; j += BLI_str_utf8_size_safe(linep->line + j)) {
227                         int chars;
228
229                         /* Mimic replacement of tabs */
230                         ch = linep->line[j];
231                         if (ch == '\t') {
232                                 chars = st->tabnumber - i % st->tabnumber;
233                                 if (linep == linein && i < cursin) cursin += chars - 1;
234                                 ch = ' ';
235                         }
236                         else {
237                                 chars = 1;
238                         }
239
240                         while (chars--) {
241                                 if (i - start >= max) {
242                                         if (chop && linep == linein && i >= cursin) {
243                                                 if (i == cursin) {
244                                                         (*offl)++;
245                                                         *offc -= end - start;
246                                                 }
247
248                                                 return;
249                                         }
250
251                                         (*offl)++;
252                                         *offc -= end - start;
253
254                                         start = end;
255                                         end += max;
256                                         chop = 1;
257                                 }
258                                 else if (ch == ' ' || ch == '-') {
259                                         end = i + 1;
260                                         chop = 0;
261                                         if (linep == linein && i >= cursin)
262                                                 return;
263                                 }
264                                 i++;
265                         }
266                 }
267                 if (linep == linein) break;
268                 linep = linep->next;
269         }
270 }
271
272 /* cursin - mem, offc - view */
273 void wrap_offset_in_line(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
274 {
275         int i, j, start, end, chars, max, chop;
276         char ch;
277
278         *offl = *offc = 0;
279
280         if (!st->text) return;
281         if (!st->wordwrap) return;
282
283         max = wrap_width(st, ar);
284
285         start = 0;
286         end = max;
287         chop = 1;
288         *offc = 0;
289         cursin = txt_utf8_offset_to_index(linein->line, cursin);
290
291         for (i = 0, j = 0; linein->line[j]; j += BLI_str_utf8_size_safe(linein->line + j)) {
292
293                 /* Mimic replacement of tabs */
294                 ch = linein->line[j];
295                 if (ch == '\t') {
296                         chars = st->tabnumber - i % st->tabnumber;
297                         if (i < cursin) cursin += chars - 1;
298                         ch = ' ';
299                 }
300                 else
301                         chars = 1;
302
303                 while (chars--) {
304                         if (i - start >= max) {
305                                 if (chop && i >= cursin) {
306                                         if (i == cursin) {
307                                                 (*offl)++;
308                                                 *offc -= end - start;
309                                         }
310
311                                         return;
312                                 }
313
314                                 (*offl)++;
315                                 *offc -= end - start;
316
317                                 start = end;
318                                 end += max;
319                                 chop = 1;
320                         }
321                         else if (ch == ' ' || ch == '-') {
322                                 end = i + 1;
323                                 chop = 0;
324                                 if (i >= cursin)
325                                         return;
326                         }
327                         i++;
328                 }
329         }
330 }
331
332 int text_get_char_pos(SpaceText *st, const char *line, int cur)
333 {
334         int a = 0, i;
335         
336         for (i = 0; i < cur && line[i]; i += BLI_str_utf8_size_safe(line + i)) {
337                 if (line[i] == '\t')
338                         a += st->tabnumber - a % st->tabnumber;
339                 else
340                         a++;
341         }
342         return a;
343 }
344
345 static const char *txt_utf8_get_nth(const char *str, int n)
346 {
347         int pos = 0;
348         while (str[pos] && n--) {
349                 pos += BLI_str_utf8_size_safe(str + pos);
350         }
351         return str + pos;
352 }
353
354 static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w, const char *format, int skip)
355 {
356         FlattenString fs;
357         int basex, i, a, start, end, max, lines; /* view */
358         int mi, ma, mstart, mend;                /* mem */
359         char fmt_prev = 0xff;
360         
361         flatten_string(st, &fs, str);
362         str = fs.buf;
363         max = w / st->cwidth;
364         if (max < 8) max = 8;
365         basex = x;
366         lines = 1;
367         
368         start = 0; mstart = 0;
369         end = max; mend = txt_utf8_get_nth(str, max) - str;
370         
371         for (i = 0, mi = 0; str[mi]; i++, mi += BLI_str_utf8_size_safe(str + mi)) {
372                 if (i - start >= max) {
373                         /* skip hidden part of line */
374                         if (skip) {
375                                 skip--;
376                                 start = end; mstart = mend;
377                                 end += max; mend = txt_utf8_get_nth(str + mend, max) - str;
378                                 continue;
379                         }
380
381                         /* Draw the visible portion of text on the overshot line */
382                         for (a = start, ma = mstart; a < end; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
383                                 if (st->showsyntax && format) {
384                                         if (fmt_prev != format[a]) format_draw_color(fmt_prev = format[a]);
385                                 }
386                                 x += text_font_draw_character_utf8(st, x, y, str + ma);
387                         }
388                         y -= st->lheight_dpi + TXT_LINE_SPACING;
389                         x = basex;
390                         lines++;
391                         start = end; mstart = mend;
392                         end += max; mend = txt_utf8_get_nth(str + mend, max) - str;
393
394                         if (y <= 0) break;
395                 }
396                 else if (str[mi] == ' ' || str[mi] == '-') {
397                         end = i + 1; mend = mi + 1;
398                 }
399         }
400
401         /* Draw the remaining text */
402         for (a = start, ma = mstart; str[ma] && y > 0; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
403                 if (st->showsyntax && format) {
404                         if (fmt_prev != format[a]) format_draw_color(fmt_prev = format[a]);
405                 }
406
407                 x += text_font_draw_character_utf8(st, x, y, str + ma);
408         }
409
410         flatten_string_free(&fs);
411
412         return lines;
413 }
414
415 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, const char *format)
416 {
417         FlattenString fs;
418         int *acc, r = 0;
419         const char *in;
420
421         int w = flatten_string(st, &fs, str);
422         if (w < cshift) {
423                 flatten_string_free(&fs);
424                 return 0; /* String is shorter than shift */
425         }
426         
427         in = txt_utf8_get_nth(fs.buf, cshift);
428         acc = fs.accum + cshift;
429         w = w - cshift;
430
431         if (draw) {
432                 int amount = maxwidth ? MIN2(w, maxwidth) : w;
433                 
434                 if (st->showsyntax && format) {
435                         int a, str_shift = 0;
436                         char fmt_prev = 0xff;
437                         format = format + cshift;
438
439                         for (a = 0; a < amount; a++) {
440                                 if (format[a] != fmt_prev) format_draw_color(fmt_prev = format[a]);
441                                 x += text_font_draw_character_utf8(st, x, y, in + str_shift);
442                                 str_shift += BLI_str_utf8_size_safe(in + str_shift);
443                         }
444                 }
445                 else {
446                         text_font_draw(st, x, y, in);
447                 }
448         }
449         else {
450                 while (w-- && *acc++ < maxwidth)
451                         r += st->cwidth;
452         }
453
454         flatten_string_free(&fs);
455
456         if (cshift && r == 0)
457                 return 0;
458         else if (st->showlinenrs)
459                 return r + TXT_OFFSET + TEXTXLOC;
460         else
461                 return r + TXT_OFFSET;
462 }
463
464 /************************ cache utilities *****************************/
465
466 typedef struct DrawCache {
467         int *line_height;
468         int total_lines, nlines;
469
470         /* this is needed to check cache relevance */
471         int winx, wordwrap, showlinenrs, tabnumber;
472         short lheight;
473         char cwidth;
474         char text_id[MAX_ID_NAME];
475
476         /* for partial lines recalculation */
477         short update_flag;
478         int valid_head, valid_tail; /* amount of unchanged lines */
479 } DrawCache;
480
481 static void text_drawcache_init(SpaceText *st)
482 {
483         DrawCache *drawcache = MEM_callocN(sizeof(DrawCache), "text draw cache");
484
485         drawcache->winx = -1;
486         drawcache->nlines = BLI_countlist(&st->text->lines);
487         drawcache->text_id[0] = '\0';
488
489         st->drawcache = drawcache;
490 }
491
492 static void text_update_drawcache(SpaceText *st, ARegion *ar)
493 {
494         DrawCache *drawcache;
495         int full_update = 0, nlines = 0;
496         Text *txt = st->text;
497
498         if (!st->drawcache) text_drawcache_init(st);
499
500         text_update_character_width(st);
501
502         drawcache = (DrawCache *)st->drawcache;
503         nlines = drawcache->nlines;
504
505         /* check if full cache update is needed */
506         full_update |= drawcache->winx != ar->winx;               /* area was resized */
507         full_update |= drawcache->wordwrap != st->wordwrap;       /* word-wrapping option was toggled */
508         full_update |= drawcache->showlinenrs != st->showlinenrs; /* word-wrapping option was toggled */
509         full_update |= drawcache->tabnumber != st->tabnumber;     /* word-wrapping option was toggled */
510         full_update |= drawcache->lheight != st->lheight_dpi;         /* word-wrapping option was toggled */
511         full_update |= drawcache->cwidth != st->cwidth;           /* word-wrapping option was toggled */
512         full_update |= strncmp(drawcache->text_id, txt->id.name, MAX_ID_NAME); /* text datablock was changed */
513
514         if (st->wordwrap) {
515                 /* update line heights */
516                 if (full_update || !drawcache->line_height) {
517                         drawcache->valid_head  = 0;
518                         drawcache->valid_tail  = 0;
519                         drawcache->update_flag = 1;
520                 }
521
522                 if (drawcache->update_flag) {
523                         TextLine *line = st->text->lines.first;
524                         int lineno = 0, size, lines_count;
525                         int *fp = drawcache->line_height, *new_tail, *old_tail;
526
527                         nlines = BLI_countlist(&txt->lines);
528                         size = sizeof(int) * nlines;
529
530                         if (fp) fp = MEM_reallocN(fp, size);
531                         else fp = MEM_callocN(size, "text drawcache line_height");
532
533                         drawcache->valid_tail = drawcache->valid_head = 0;
534                         old_tail = fp + drawcache->nlines - drawcache->valid_tail;
535                         new_tail = fp + nlines - drawcache->valid_tail;
536                         memmove(new_tail, old_tail, drawcache->valid_tail);
537
538                         drawcache->total_lines = 0;
539
540                         if (st->showlinenrs)
541                                 st->linenrs_tot = (int)floor(log10((float)nlines)) + 1;
542
543                         while (line) {
544                                 if (drawcache->valid_head) { /* we're inside valid head lines */
545                                         lines_count = fp[lineno];
546                                         drawcache->valid_head--;
547                                 }
548                                 else if (lineno > new_tail - fp) {  /* we-re inside valid tail lines */
549                                         lines_count = fp[lineno];
550                                 }
551                                 else {
552                                         lines_count = text_get_visible_lines(st, ar, line->line);
553                                 }
554
555                                 fp[lineno] = lines_count;
556
557                                 line = line->next;
558                                 lineno++;
559                                 drawcache->total_lines += lines_count;
560                         }
561
562                         drawcache->line_height = fp;
563                 }
564         }
565         else {
566                 if (drawcache->line_height) {
567                         MEM_freeN(drawcache->line_height);
568                         drawcache->line_height = NULL;
569                 }
570
571                 if (full_update || drawcache->update_flag) {
572                         nlines = BLI_countlist(&txt->lines);
573
574                         if (st->showlinenrs)
575                                 st->linenrs_tot = (int)floor(log10((float)nlines)) + 1;
576                 }
577
578                 drawcache->total_lines = nlines;
579         }
580
581         drawcache->nlines = nlines;
582
583         /* store settings */
584         drawcache->winx        = ar->winx;
585         drawcache->wordwrap    = st->wordwrap;
586         drawcache->lheight     = st->lheight_dpi;
587         drawcache->cwidth      = st->cwidth;
588         drawcache->showlinenrs = st->showlinenrs;
589         drawcache->tabnumber   = st->tabnumber;
590
591         strncpy(drawcache->text_id, txt->id.name, MAX_ID_NAME);
592
593         /* clear update flag */
594         drawcache->update_flag = 0;
595         drawcache->valid_head  = 0;
596         drawcache->valid_tail  = 0;
597 }
598
599 void text_drawcache_tag_update(SpaceText *st, int full)
600 {
601         /* this happens if text editor ops are caled from python */
602         if (st == NULL)
603                 return;
604                 
605         if (st->drawcache) {
606                 DrawCache *drawcache = (DrawCache *)st->drawcache;
607                 Text *txt = st->text;
608
609                 if (drawcache->update_flag) {
610                         /* happens when tagging update from space listener */
611                         /* should do nothing to prevent locally tagged cache be fully recalculated */
612                         return;
613                 }
614
615                 if (!full) {
616                         int sellno = BLI_findindex(&txt->lines, txt->sell);
617                         int curlno = BLI_findindex(&txt->lines, txt->curl);
618
619                         if (curlno < sellno) {
620                                 drawcache->valid_head = curlno;
621                                 drawcache->valid_tail = drawcache->nlines - sellno - 1;
622                         }
623                         else {
624                                 drawcache->valid_head = sellno;
625                                 drawcache->valid_tail = drawcache->nlines - curlno - 1;
626                         }
627
628                         /* quick cache recalculation is also used in delete operator,
629                          * which could merge lines which are adjacent to current selection lines
630                          * expand recalculate area to this lines */
631                         if (drawcache->valid_head > 0) drawcache->valid_head--;
632                         if (drawcache->valid_tail > 0) drawcache->valid_tail--;
633                 }
634                 else {
635                         drawcache->valid_head = 0;
636                         drawcache->valid_tail = 0;
637                 }
638
639                 drawcache->update_flag = 1;
640         }
641 }
642
643 void text_free_caches(SpaceText *st)
644 {
645         DrawCache *drawcache = (DrawCache *)st->drawcache;
646
647         if (drawcache) {
648                 if (drawcache->line_height)
649                         MEM_freeN(drawcache->line_height);
650
651                 MEM_freeN(drawcache);
652         }
653 }
654
655 /************************ word-wrap utilities *****************************/
656
657 /* cache should be updated in caller */
658 static int text_get_visible_lines_no(SpaceText *st, int lineno)
659 {
660         DrawCache *drawcache = (DrawCache *)st->drawcache;
661
662         return drawcache->line_height[lineno];
663 }
664
665 int text_get_visible_lines(SpaceText *st, ARegion *ar, const char *str)
666 {
667         int i, j, start, end, max, lines, chars;
668         char ch;
669
670         max = wrap_width(st, ar);
671         lines = 1;
672         start = 0;
673         end = max;
674         for (i = 0, j = 0; str[j]; j += BLI_str_utf8_size_safe(str + j)) {
675                 /* Mimic replacement of tabs */
676                 ch = str[j];
677                 if (ch == '\t') {
678                         chars = st->tabnumber - i % st->tabnumber;
679                         ch = ' ';
680                 }
681                 else {
682                         chars = 1;
683                 }
684
685                 while (chars--) {
686                         if (i - start >= max) {
687                                 lines++;
688                                 start = end;
689                                 end += max;
690                         }
691                         else if (ch == ' ' || ch == '-') {
692                                 end = i + 1;
693                         }
694
695                         i++;
696                 }
697         }
698
699         return lines;
700 }
701
702 int text_get_span_wrap(SpaceText *st, ARegion *ar, TextLine *from, TextLine *to)
703 {
704         if (st->wordwrap) {
705                 int ret = 0;
706                 TextLine *tmp = from;
707
708                 /* Look forwards */
709                 while (tmp) {
710                         if (tmp == to) return ret;
711                         ret += text_get_visible_lines(st, ar, tmp->line);
712                         tmp = tmp->next;
713                 }
714
715                 return ret;
716         }
717         else {
718                 return txt_get_span(from, to);
719         }
720 }
721
722 int text_get_total_lines(SpaceText *st, ARegion *ar)
723 {
724         DrawCache *drawcache;
725
726         text_update_drawcache(st, ar);
727         drawcache = (DrawCache *)st->drawcache;
728
729         return drawcache->total_lines;
730 }
731
732 /************************ draw scrollbar *****************************/
733
734 static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll, rcti *back)
735 {
736         int lhlstart, lhlend, ltexth, sell_off, curl_off;
737         short barheight, barstart, hlstart, hlend, blank_lines;
738         short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
739
740         pix_top_margin = 8;
741         pix_bottom_margin = 4;
742         pix_available = ar->winy - pix_top_margin - pix_bottom_margin;
743         ltexth = text_get_total_lines(st, ar);
744         blank_lines = st->viewlines / 2;
745         
746         /* nicer code: use scroll rect for entire bar */
747         back->xmin = ar->winx - (V2D_SCROLL_WIDTH + 1);
748         back->xmax = ar->winx;
749         back->ymin = 0;
750         back->ymax = ar->winy;
751         
752         scroll->xmin = ar->winx - V2D_SCROLL_WIDTH;
753         scroll->xmax = ar->winx - 5;
754         scroll->ymin = 4;
755         scroll->ymax = 4 + pix_available;
756         
757         /* when re-sizing a view-port with the bar at the bottom to a greater height more blank lines will be added */
758         if (ltexth + blank_lines < st->top + st->viewlines) {
759                 blank_lines = st->top + st->viewlines - ltexth;
760         }
761         
762         ltexth += blank_lines;
763
764         barheight = (ltexth > 0) ? (st->viewlines * pix_available) / ltexth : 0;
765         pix_bardiff = 0;
766         if (barheight < 20) {
767                 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */
768                 barheight = 20;
769         }
770         barstart = (ltexth > 0) ? ((pix_available - pix_bardiff) * st->top) / ltexth : 0;
771
772         st->txtbar = *scroll;
773         st->txtbar.ymax -= barstart;
774         st->txtbar.ymin = st->txtbar.ymax - barheight;
775
776         CLAMP(st->txtbar.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
777         CLAMP(st->txtbar.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
778
779         st->pix_per_line = (pix_available > 0) ? (float) ltexth / pix_available : 0;
780         if (st->pix_per_line < 0.1f) st->pix_per_line = 0.1f;
781
782         curl_off = text_get_span_wrap(st, ar, st->text->lines.first, st->text->curl);
783         sell_off = text_get_span_wrap(st, ar, st->text->lines.first, st->text->sell);
784         lhlstart = MIN2(curl_off, sell_off);
785         lhlend = MAX2(curl_off, sell_off);
786
787         if (ltexth > 0) {
788                 hlstart = (lhlstart * pix_available) / ltexth;
789                 hlend = (lhlend * pix_available) / ltexth;
790
791                 /* the scrollbar is non-linear sized */
792                 if (pix_bardiff > 0) {
793                         /* the start of the highlight is in the current viewport */
794                         if (ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) {
795                                 /* speed the progresion of the start of the highlight through the scrollbar */
796                                 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);
797                         }
798                         else if (lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
799                                 /* push hl start down */
800                                 hlstart = barstart + barheight;
801                         }
802                         else if (lhlend > st->top && lhlstart < st->top && hlstart > barstart) {
803                                 /*fill out start */
804                                 hlstart = barstart;
805                         }
806
807                         if (hlend <= hlstart) {
808                                 hlend = hlstart + 2;
809                         }
810
811                         /* the end of the highlight is in the current viewport */
812                         if (ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) {
813                                 /* speed the progresion of the end of the highlight through the scrollbar */
814                                 hlend = (((pix_available - pix_bardiff) * lhlend) / ltexth) + (pix_bardiff * (lhlend - st->top) / st->viewlines);
815                         }
816                         else if (lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
817                                 /* push hl end up */
818                                 hlend = barstart;
819                         }
820                         else if (lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
821                                 /* fill out end */
822                                 hlend = barstart + barheight;
823                         }
824
825                         if (hlend <= hlstart) {
826                                 hlstart = hlend - 2;
827                         }
828                 }
829         }
830         else {
831                 hlstart = 0;
832                 hlend = 0;
833         }
834
835         if (hlend - hlstart < 2) {
836                 hlend = hlstart + 2;
837         }
838         
839         st->txtscroll = *scroll;
840         st->txtscroll.ymax = ar->winy - pix_top_margin - hlstart;
841         st->txtscroll.ymin = ar->winy - pix_top_margin - hlend;
842
843         CLAMP(st->txtscroll.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
844         CLAMP(st->txtscroll.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
845 }
846
847 static void draw_textscroll(SpaceText *st, rcti *scroll, rcti *back)
848 {
849         bTheme *btheme = UI_GetTheme();
850         uiWidgetColors wcol = btheme->tui.wcol_scroll;
851         unsigned char col[4];
852         float rad;
853         
854         UI_ThemeColor(TH_BACK);
855         glRecti(back->xmin, back->ymin, back->xmax, back->ymax);
856
857         uiWidgetScrollDraw(&wcol, scroll, &st->txtbar, (st->flags & ST_SCROLL_SELECT) ? UI_SCROLL_PRESSED : 0);
858
859         uiSetRoundBox(UI_CNR_ALL);
860         rad = 0.4f * min_ii(BLI_rcti_size_x(&st->txtscroll), BLI_rcti_size_y(&st->txtscroll));
861         UI_GetThemeColor3ubv(TH_HILITE, col);
862         col[3] = 48;
863         glColor4ubv(col);
864         glEnable(GL_BLEND);
865         uiRoundBox(st->txtscroll.xmin + 1, st->txtscroll.ymin, st->txtscroll.xmax - 1, st->txtscroll.ymax, rad);
866         glDisable(GL_BLEND);
867 }
868
869 /*********************** draw documentation *******************************/
870
871 static void draw_documentation(SpaceText *st, ARegion *ar)
872 {
873         TextLine *tmp;
874         char *docs, buf[DOC_WIDTH + 1], *p;
875         int i, br, lines;
876         int boxw, boxh, l, x, y /* , top */ /* UNUSED */;
877         
878         if (!st || !st->text) return;
879         if (!texttool_text_is_active(st->text)) return;
880         
881         docs = texttool_docs_get();
882
883         if (!docs) return;
884
885         /* Count the visible lines to the cursor */
886         for (tmp = st->text->curl, l = -st->top; tmp; tmp = tmp->prev, l++) ;
887         if (l < 0) return;
888         
889         if (st->showlinenrs) {
890                 x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET + TEXTXLOC - 4;
891         }
892         else {
893                 x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET - 4;
894         }
895         if (texttool_suggest_first()) {
896                 x += SUGG_LIST_WIDTH * st->cwidth + 50;
897         }
898
899         /* top = */ /* UNUSED */ y = ar->winy - st->lheight_dpi * l - 2;
900         boxw = DOC_WIDTH * st->cwidth + 20;
901         boxh = (DOC_HEIGHT + 1) * (st->lheight_dpi + TXT_LINE_SPACING);
902
903         /* Draw panel */
904         UI_ThemeColor(TH_BACK);
905         glRecti(x, y, x + boxw, y - boxh);
906         UI_ThemeColor(TH_SHADE1);
907         glBegin(GL_LINE_LOOP);
908         glVertex2i(x, y);
909         glVertex2i(x + boxw, y);
910         glVertex2i(x + boxw, y - boxh);
911         glVertex2i(x, y - boxh);
912         glEnd();
913         glBegin(GL_LINE_LOOP);
914         glVertex2i(x + boxw - 10, y - 7);
915         glVertex2i(x + boxw - 4, y - 7);
916         glVertex2i(x + boxw - 7, y - 2);
917         glEnd();
918         glBegin(GL_LINE_LOOP);
919         glVertex2i(x + boxw - 10, y - boxh + 7);
920         glVertex2i(x + boxw - 4, y - boxh + 7);
921         glVertex2i(x + boxw - 7, y - boxh + 2);
922         glEnd();
923         UI_ThemeColor(TH_TEXT);
924
925         i = 0; br = DOC_WIDTH; lines = 0; // XXX -doc_scroll;
926         for (p = docs; *p; p++) {
927                 if (*p == '\r' && *(++p) != '\n') *(--p) = '\n';  /* Fix line endings */
928                 if (*p == ' ' || *p == '\t')
929                         br = i;
930                 else if (*p == '\n') {
931                         buf[i] = '\0';
932                         if (lines >= 0) {
933                                 y -= st->lheight_dpi;
934                                 text_draw(st, buf, 0, 0, 1, x + 4, y - 3, NULL);
935                         }
936                         i = 0; br = DOC_WIDTH; lines++;
937                 }
938                 buf[i++] = *p;
939                 if (i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
940                         buf[br] = '\0';
941                         if (lines >= 0) {
942                                 y -= st->lheight_dpi;
943                                 text_draw(st, buf, 0, 0, 1, x + 4, y - 3, NULL);
944                         }
945                         p -= i - br - 1; /* Rewind pointer to last break */
946                         i = 0; br = DOC_WIDTH; lines++;
947                 }
948                 if (lines >= DOC_HEIGHT) break;
949         }
950
951         if (0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) {
952                 // XXX doc_scroll--;
953                 draw_documentation(st, ar);
954         }
955 }
956
957 /*********************** draw suggestion list *******************************/
958
959 static void draw_suggestion_list(SpaceText *st, ARegion *ar)
960 {
961         SuggItem *item, *first, *last, *sel;
962         TextLine *tmp;
963         char str[SUGG_LIST_WIDTH + 1];
964         int w, boxw = 0, boxh, i, l, x, y, *top;
965         const int lheight = st->lheight_dpi + TXT_LINE_SPACING;
966         const int margin_x = 2;
967         
968         if (!st->text) return;
969         if (!texttool_text_is_active(st->text)) return;
970
971         first = texttool_suggest_first();
972         last = texttool_suggest_last();
973
974         if (!first || !last) return;
975
976         text_pop_suggest_list();
977         sel = texttool_suggest_selected();
978         top = texttool_suggest_top();
979
980         /* Count the visible lines to the cursor */
981         for (tmp = st->text->curl, l = -st->top; tmp; tmp = tmp->prev, l++) ;
982         if (l < 0) return;
983         
984         if (st->showlinenrs) {
985                 x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET + TEXTXLOC - 4;
986         }
987         else {
988                 x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET - 4;
989         }
990         /* offset back so the start of the text lines up with the suggestions,
991          * not essential but makes suggestions easier to follow */
992         x -= st->cwidth * (st->text->curc - text_find_identifier_start(st->text->curl->line, st->text->curc));
993         y = ar->winy - lheight * l - 2;
994
995         boxw = SUGG_LIST_WIDTH * st->cwidth + 20;
996         boxh = SUGG_LIST_SIZE * lheight + 8;
997         
998         /* not needed but stands out nicer */
999         uiDrawBoxShadow(220, x, y - boxh, x + boxw, y);
1000
1001         UI_ThemeColor(TH_SHADE1);
1002         glRecti(x - 1, y + 1, x + boxw + 1, y - boxh - 1);
1003         UI_ThemeColorShade(TH_BACK, 16);
1004         glRecti(x, y, x + boxw, y - boxh);
1005
1006         /* Set the top 'item' of the visible list */
1007         for (i = 0, item = first; i < *top && item->next; i++, item = item->next) ;
1008
1009         for (i = 0; i < SUGG_LIST_SIZE && item; i++, item = item->next) {
1010
1011                 y -= lheight;
1012
1013                 BLI_strncpy(str, item->name, SUGG_LIST_WIDTH);
1014
1015                 w = BLF_width(mono, str);
1016                 
1017                 if (item == sel) {
1018                         UI_ThemeColor(TH_SHADE2);
1019                         glRecti(x + margin_x, y - 3, x + margin_x + w, y + lheight - 3);
1020                 }
1021
1022                 format_draw_color(item->type);
1023                 text_draw(st, str, 0, 0, 1, x + margin_x, y - 1, NULL);
1024
1025                 if (item == last) break;
1026         }
1027 }
1028
1029 /*********************** draw cursor ************************/
1030
1031 static void draw_cursor(SpaceText *st, ARegion *ar)
1032 {
1033         Text *text = st->text;
1034         int vcurl, vcurc, vsell, vselc, hidden = 0;
1035         int x, y, w, i;
1036         const int lheight = st->lheight_dpi + TXT_LINE_SPACING;
1037
1038         /* Draw the selection */
1039         if (text->curl != text->sell || text->curc != text->selc) {
1040                 int offl, offc;
1041                 /* Convert all to view space character coordinates */
1042                 wrap_offset(st, ar, text->curl, text->curc, &offl, &offc);
1043                 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
1044                 vcurc = text_get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
1045                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1046                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1047                 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1048
1049                 if (vcurc < 0) vcurc = 0;
1050                 if (vselc < 0) vselc = 0, hidden = 1;
1051                 
1052                 UI_ThemeColor(TH_SHADE2);
1053                 x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1054                 y = ar->winy;
1055
1056                 if (vcurl == vsell) {
1057                         y -= vcurl * lheight;
1058                         if (vcurc < vselc)
1059                                 glRecti(x + vcurc * st->cwidth - 1, y, x + vselc * st->cwidth, y - lheight);
1060                         else
1061                                 glRecti(x + vselc * st->cwidth - 1, y, x + vcurc * st->cwidth, y - lheight);
1062                 }
1063                 else {
1064                         int froml, fromc, tol, toc;
1065
1066                         if (vcurl < vsell) {
1067                                 froml = vcurl; tol = vsell;
1068                                 fromc = vcurc; toc = vselc;
1069                         }
1070                         else {
1071                                 froml = vsell; tol = vcurl;
1072                                 fromc = vselc; toc = vcurc;
1073                         }
1074
1075                         y -= froml * lheight;
1076                         glRecti(x + fromc * st->cwidth - 1, y, ar->winx, y - lheight); y -= lheight;
1077                         for (i = froml + 1; i < tol; i++)
1078                                 glRecti(x - 4, y, ar->winx, y - lheight),  y -= lheight;
1079
1080                         glRecti(x - 4, y, x + toc * st->cwidth, y - lheight);  y -= lheight;
1081                 }
1082         }
1083         else {
1084                 int offl, offc;
1085                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1086                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1087                 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1088
1089                 if (vselc < 0) {
1090                         vselc = 0;
1091                         hidden = 1;
1092                 }
1093         }
1094
1095         if (st->line_hlight) {
1096                 int x1, x2, y1, y2;
1097
1098                 if (st->wordwrap) {
1099                         int visible_lines = text_get_visible_lines(st, ar, text->sell->line);
1100                         int offl, offc;
1101
1102                         wrap_offset_in_line(st, ar, text->sell, text->selc, &offl, &offc);
1103
1104                         y1 = ar->winy - 2 - (vsell - offl) * lheight;
1105                         y2 = y1 - (lheight * visible_lines + TXT_LINE_SPACING);
1106                 }
1107                 else {
1108                         y1 = ar->winy - 2 - vsell * lheight;
1109                         y2 = y1 - (lheight + TXT_LINE_SPACING);
1110                 }
1111
1112                 if (!(y1 < 0 || y2 > ar->winy)) { /* check we need to draw */
1113                         x1 = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1114                         x2 = x1 + ar->winx;
1115
1116                         glColor4ub(255, 255, 255, 32);
1117                         
1118                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1119                         glEnable(GL_BLEND);
1120                         glRecti(x1 - 4, y1, x2, y2 + TXT_LINE_SPACING);
1121                         glDisable(GL_BLEND);
1122                 }
1123         }
1124         
1125         if (!hidden) {
1126                 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
1127                 x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1128                 x += vselc * st->cwidth;
1129                 y = ar->winy - vsell * lheight;
1130                 
1131                 if (st->overwrite) {
1132                         char ch = text->sell->line[text->selc];
1133                         
1134                         y += TXT_LINE_SPACING;
1135                         w = st->cwidth;
1136                         if (ch == '\t') w *= st->tabnumber - (vselc + st->left) % st->tabnumber;
1137                         
1138                         UI_ThemeColor(TH_HILITE);
1139                         glRecti(x, y - lheight - 1, x + w, y - lheight + 1);
1140                 }
1141                 else {
1142                         UI_ThemeColor(TH_HILITE);
1143                         glRecti(x - 1, y, x + 1, y - lheight);
1144                 }
1145         }
1146 }
1147
1148 /******************* draw matching brackets *********************/
1149
1150 static void draw_brackets(SpaceText *st, ARegion *ar)
1151 {
1152         TextLine *startl, *endl, *linep;
1153         Text *text = st->text;
1154         int b, fc, find, stack, viewc, viewl, offl, offc, x, y;
1155         int startc, endc, c;
1156         
1157         char ch;
1158
1159         // showsyntax must be on or else the format string will be null
1160         if (!text->curl || !st->showsyntax) return;
1161
1162         startl = text->curl;
1163         startc = text->curc;
1164         b = text_check_bracket(startl->line[startc]);
1165         if (b == 0 && startc > 0) b = text_check_bracket(startl->line[--startc]);
1166         if (b == 0) return;
1167
1168         linep = startl;
1169         c = startc;
1170         fc = txt_utf8_offset_to_index(linep->line, startc);
1171         endl = NULL;
1172         endc = -1;
1173         find = -b;
1174         stack = 0;
1175         
1176         /* Don't highlight backets if syntax HL is off or bracket in string or comment. */
1177         if (!linep->format || linep->format[fc] == FMT_TYPE_STRING || linep->format[fc] == FMT_TYPE_COMMENT)
1178                 return;
1179
1180         if (b > 0) {
1181                 /* opening bracket, search forward for close */
1182                 fc++;
1183                 c += BLI_str_utf8_size_safe(linep->line + c);
1184                 while (linep) {
1185                         while (c < linep->len) {
1186                                 if (linep->format && linep->format[fc] != FMT_TYPE_STRING && linep->format[fc] != FMT_TYPE_COMMENT) {
1187                                         b = text_check_bracket(linep->line[c]);
1188                                         if (b == find) {
1189                                                 if (stack == 0) {
1190                                                         endl = linep;
1191                                                         endc = c;
1192                                                         break;
1193                                                 }
1194                                                 stack--;
1195                                         }
1196                                         else if (b == -find) {
1197                                                 stack++;
1198                                         }
1199                                 }
1200                                 fc++;
1201                                 c += BLI_str_utf8_size_safe(linep->line + c);
1202                         }
1203                         if (endl) break;
1204                         linep = linep->next;
1205                         c = 0;
1206                         fc = 0;
1207                 }
1208         }
1209         else {
1210                 /* closing bracket, search backward for open */
1211                 fc--;
1212                 if (c > 0) c -= linep->line + c - BLI_str_prev_char_utf8(linep->line + c);
1213                 while (linep) {
1214                         while (fc >= 0) {
1215                                 if (linep->format && linep->format[fc] != FMT_TYPE_STRING && linep->format[fc] != FMT_TYPE_COMMENT) {
1216                                         b = text_check_bracket(linep->line[c]);
1217                                         if (b == find) {
1218                                                 if (stack == 0) {
1219                                                         endl = linep;
1220                                                         endc = c;
1221                                                         break;
1222                                                 }
1223                                                 stack--;
1224                                         }
1225                                         else if (b == -find) {
1226                                                 stack++;
1227                                         }
1228                                 }
1229                                 fc--;
1230                                 if (c > 0) c -= linep->line + c - BLI_str_prev_char_utf8(linep->line + c);
1231                         }
1232                         if (endl) break;
1233                         linep = linep->prev;
1234                         if (linep) {
1235                                 if (linep->format) fc = strlen(linep->format) - 1;
1236                                 else fc = -1;
1237                                 if (linep->len) c = BLI_str_prev_char_utf8(linep->line + linep->len) - linep->line;
1238                                 else fc = -1;
1239                         }
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_dpi;
1249
1250         /* draw opening bracket */
1251         ch = startl->line[startc];
1252         wrap_offset(st, ar, startl, startc, &offl, &offc);
1253         viewc = text_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 * st->cwidth, y - viewl * (st->lheight_dpi + TXT_LINE_SPACING), ch);
1259                 text_font_draw_character(st, x + viewc * st->cwidth + 1, y - viewl * (st->lheight_dpi + TXT_LINE_SPACING), ch);
1260         }
1261
1262         /* draw closing bracket */
1263         ch = endl->line[endc];
1264         wrap_offset(st, ar, endl, endc, &offl, &offc);
1265         viewc = text_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 * st->cwidth, y - viewl * (st->lheight_dpi + TXT_LINE_SPACING), ch);
1271                 text_font_draw_character(st, x + viewc * st->cwidth + 1, y - viewl * (st->lheight_dpi + TXT_LINE_SPACING), 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         TextFormatType *tft;
1281         TextLine *tmp;
1282         rcti scroll, back;
1283         char linenr[12];
1284         int i, x, y, winx, linecount = 0, lineno = 0;
1285         int wraplinecount = 0, wrap_skip = 0;
1286         int margin_column_x;
1287
1288         /* dpi controlled line height and font size */
1289         st->lheight_dpi = (U.widget_unit * st->lheight) / 20;
1290         
1291         if (st->lheight_dpi) st->viewlines = (int)ar->winy / (st->lheight_dpi + TXT_LINE_SPACING);
1292         else st->viewlines = 0;
1293
1294         /* if no text, nothing to do */
1295         if (!text)
1296                 return;
1297         
1298         text_update_drawcache(st, ar);
1299
1300         /* make sure all the positional pointers exist */
1301         if (!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1302                 txt_clean_text(text);
1303         
1304         /* update rects for scroll */
1305         calc_text_rcts(st, ar, &scroll, &back); /* scroll will hold the entire bar size */
1306
1307         /* update syntax formatting if needed */
1308         tft = ED_text_format_get(text);
1309         tmp = text->lines.first;
1310         lineno = 0;
1311         for (i = 0; i < st->top && tmp; i++) {
1312                 if (st->showsyntax && !tmp->format)
1313                         tft->format_line(st, tmp, 0);
1314
1315                 if (st->wordwrap) {
1316                         int lines = text_get_visible_lines_no(st, lineno);
1317
1318                         if (wraplinecount + lines > st->top) {
1319                                 wrap_skip = st->top - wraplinecount;
1320                                 break;
1321                         }
1322                         else {
1323                                 wraplinecount += lines;
1324                                 tmp = tmp->next;
1325                                 linecount++;
1326                         }
1327                 }
1328                 else {
1329                         tmp = tmp->next;
1330                         linecount++;
1331                 }
1332
1333                 lineno++;
1334         }
1335
1336         text_font_begin(st);
1337         st->cwidth = BLF_fixed_width(mono);
1338         st->cwidth = MAX2(st->cwidth, (char)1);
1339
1340         /* draw line numbers background */
1341         if (st->showlinenrs) {
1342                 x = TXT_OFFSET + TEXTXLOC;
1343
1344                 UI_ThemeColor(TH_GRID);
1345                 glRecti((TXT_OFFSET - 12), 0, (TXT_OFFSET - 5) + TEXTXLOC, ar->winy - 2);
1346         }
1347         else {
1348                 st->linenrs_tot = 0; /* not used */
1349                 x = TXT_OFFSET;
1350         }
1351         y = ar->winy - st->lheight_dpi;
1352         winx = ar->winx - TXT_SCROLL_WIDTH;
1353         
1354         /* draw cursor */
1355         draw_cursor(st, ar);
1356
1357         /* draw the text */
1358         UI_ThemeColor(TH_TEXT);
1359
1360         for (i = 0; y > 0 && i < st->viewlines && tmp; i++, tmp = tmp->next) {
1361                 if (st->showsyntax && !tmp->format)
1362                         tft->format_line(st, tmp, 0);
1363
1364                 if (st->showlinenrs && !wrap_skip) {
1365                         /* draw line number */
1366                         if (tmp == text->curl)
1367                                 UI_ThemeColor(TH_HILITE);
1368                         else
1369                                 UI_ThemeColor(TH_TEXT);
1370
1371                         BLI_snprintf(linenr, sizeof(linenr), "%*d", st->linenrs_tot, i + linecount + 1);
1372                         /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */
1373                         text_font_draw(st, TXT_OFFSET - 7, y, linenr);
1374
1375                         UI_ThemeColor(TH_TEXT);
1376                 }
1377
1378                 if (st->wordwrap) {
1379                         /* draw word wrapped text */
1380                         int lines = text_draw_wrapped(st, tmp->line, x, y, winx - x, tmp->format, wrap_skip);
1381                         y -= lines * (st->lheight_dpi + TXT_LINE_SPACING);
1382                 }
1383                 else {
1384                         /* draw unwrapped text */
1385                         text_draw(st, tmp->line, st->left, ar->winx / st->cwidth, 1, x, y, tmp->format);
1386                         y -= st->lheight_dpi + TXT_LINE_SPACING;
1387                 }
1388                 
1389                 wrap_skip = 0;
1390         }
1391         
1392         if (st->flags & ST_SHOW_MARGIN) {
1393                 UI_ThemeColor(TH_HILITE);
1394
1395                 margin_column_x = x + st->cwidth * (st->margin_column - st->left);
1396                 
1397                 if (margin_column_x >= x) {
1398                         glBegin(GL_LINES);
1399                         glVertex2i(margin_column_x, 0);
1400                         glVertex2i(margin_column_x, ar->winy - 2);
1401                         glEnd();
1402                 }
1403         }
1404
1405         /* draw other stuff */
1406         draw_brackets(st, ar);
1407         glTranslatef(GLA_PIXEL_OFS, GLA_PIXEL_OFS, 0.0f); /* XXX scroll requires exact pixel space */
1408         draw_textscroll(st, &scroll, &back);
1409         draw_documentation(st, ar);
1410         draw_suggestion_list(st, ar);
1411         
1412         text_font_end(st);
1413 }
1414
1415 /************************** update ***************************/
1416
1417 void text_update_character_width(SpaceText *st)
1418 {
1419         text_font_begin(st);
1420         st->cwidth = BLF_fixed_width(mono);
1421         st->cwidth = MAX2(st->cwidth, (char)1);
1422         text_font_end(st);
1423 }
1424
1425 /* Moves the view to the cursor location,
1426  * also used to make sure the view isn't outside the file */
1427 void text_scroll_to_cursor(SpaceText *st, ScrArea *sa)
1428 {
1429         Text *text;
1430         ARegion *ar = NULL;
1431         int i, x, winx = 0;
1432
1433         if (ELEM3(NULL, st, st->text, st->text->curl)) return;
1434
1435         text = st->text;
1436
1437         for (ar = sa->regionbase.first; ar; ar = ar->next)
1438                 if (ar->regiontype == RGN_TYPE_WINDOW) {
1439                         winx = ar->winx;
1440                         break;
1441                 }
1442         
1443         winx -= TXT_SCROLL_WIDTH;
1444
1445         text_update_character_width(st);
1446
1447         i = txt_get_span(text->lines.first, text->sell);
1448         if (st->wordwrap) {
1449                 int offl, offc;
1450                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1451                 i += offl;
1452         }
1453
1454         if (st->top + st->viewlines <= i || st->top > i)
1455                 st->top = i - st->viewlines / 2;
1456         
1457         if (st->wordwrap) {
1458                 st->left = 0;
1459         }
1460         else {
1461                 x = text_draw(st, text->sell->line, st->left, text->selc, 0, 0, 0, NULL);
1462
1463                 if (x == 0 || x > winx)
1464                         st->left = text->curc - 0.5 * winx / st->cwidth;
1465         }
1466
1467         if (st->top < 0) st->top = 0;
1468         if (st->left < 0) st->left = 0;
1469 }
1470
1471 void text_update_cursor_moved(bContext *C)
1472 {
1473         ScrArea *sa = CTX_wm_area(C);
1474         SpaceText *st = CTX_wm_space_text(C);
1475
1476         text_scroll_to_cursor(st, sa);
1477 }