text autocomplete
[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 chars = 1;
682
683                 while (chars--) {
684                         if (i - start >= max) {
685                                 lines++;
686                                 start = end;
687                                 end += max;
688                         }
689                         else if (ch == ' ' || ch == '-') {
690                                 end = i + 1;
691                         }
692
693                         i++;
694                 }
695         }
696
697         return lines;
698 }
699
700 int text_get_span_wrap(SpaceText *st, ARegion *ar, TextLine *from, TextLine *to)
701 {
702         if (st->wordwrap) {
703                 int ret = 0;
704                 TextLine *tmp = from;
705
706                 /* Look forwards */
707                 while (tmp) {
708                         if (tmp == to) return ret;
709                         ret += text_get_visible_lines(st, ar, tmp->line);
710                         tmp = tmp->next;
711                 }
712
713                 return ret;
714         }
715         else return txt_get_span(from, to);
716 }
717
718 int text_get_total_lines(SpaceText *st, ARegion *ar)
719 {
720         DrawCache *drawcache;
721
722         text_update_drawcache(st, ar);
723         drawcache = (DrawCache *)st->drawcache;
724
725         return drawcache->total_lines;
726 }
727
728 /************************ draw scrollbar *****************************/
729
730 static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll, rcti *back)
731 {
732         int lhlstart, lhlend, ltexth, sell_off, curl_off;
733         short barheight, barstart, hlstart, hlend, blank_lines;
734         short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
735
736         pix_top_margin = 8;
737         pix_bottom_margin = 4;
738         pix_available = ar->winy - pix_top_margin - pix_bottom_margin;
739         ltexth = text_get_total_lines(st, ar);
740         blank_lines = st->viewlines / 2;
741         
742         /* nicer code: use scroll rect for entire bar */
743         back->xmin = ar->winx - (V2D_SCROLL_WIDTH + 1);
744         back->xmax = ar->winx;
745         back->ymin = 0;
746         back->ymax = ar->winy;
747         
748         scroll->xmin = ar->winx - V2D_SCROLL_WIDTH;
749         scroll->xmax = ar->winx - 5;
750         scroll->ymin = 4;
751         scroll->ymax = 4 + pix_available;
752         
753         /* when re-sizing a view-port with the bar at the bottom to a greater height more blank lines will be added */
754         if (ltexth + blank_lines < st->top + st->viewlines) {
755                 blank_lines = st->top + st->viewlines - ltexth;
756         }
757         
758         ltexth += blank_lines;
759
760         barheight = (ltexth > 0) ? (st->viewlines * pix_available) / ltexth : 0;
761         pix_bardiff = 0;
762         if (barheight < 20) {
763                 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */
764                 barheight = 20;
765         }
766         barstart = (ltexth > 0) ? ((pix_available - pix_bardiff) * st->top) / ltexth : 0;
767
768         st->txtbar = *scroll;
769         st->txtbar.ymax -= barstart;
770         st->txtbar.ymin = st->txtbar.ymax - barheight;
771
772         CLAMP(st->txtbar.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
773         CLAMP(st->txtbar.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
774
775         st->pix_per_line = (pix_available > 0) ? (float) ltexth / pix_available : 0;
776         if (st->pix_per_line < 0.1f) st->pix_per_line = 0.1f;
777
778         curl_off = text_get_span_wrap(st, ar, st->text->lines.first, st->text->curl);
779         sell_off = text_get_span_wrap(st, ar, st->text->lines.first, st->text->sell);
780         lhlstart = MIN2(curl_off, sell_off);
781         lhlend = MAX2(curl_off, sell_off);
782
783         if (ltexth > 0) {
784                 hlstart = (lhlstart * pix_available) / ltexth;
785                 hlend = (lhlend * pix_available) / ltexth;
786
787                 /* the scrollbar is non-linear sized */
788                 if (pix_bardiff > 0) {
789                         /* the start of the highlight is in the current viewport */
790                         if (ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) {
791                                 /* speed the progresion of the start of the highlight through the scrollbar */
792                                 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);
793                         }
794                         else if (lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
795                                 /* push hl start down */
796                                 hlstart = barstart + barheight;
797                         }
798                         else if (lhlend > st->top && lhlstart < st->top && hlstart > barstart) {
799                                 /*fill out start */
800                                 hlstart = barstart;
801                         }
802
803                         if (hlend <= hlstart) {
804                                 hlend = hlstart + 2;
805                         }
806
807                         /* the end of the highlight is in the current viewport */
808                         if (ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) {
809                                 /* speed the progresion of the end of the highlight through the scrollbar */
810                                 hlend = (((pix_available - pix_bardiff) * lhlend) / ltexth) + (pix_bardiff * (lhlend - st->top) / st->viewlines);
811                         }
812                         else if (lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
813                                 /* push hl end up */
814                                 hlend = barstart;
815                         }
816                         else if (lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
817                                 /* fill out end */
818                                 hlend = barstart + barheight;
819                         }
820
821                         if (hlend <= hlstart) {
822                                 hlstart = hlend - 2;
823                         }
824                 }
825         }
826         else {
827                 hlstart = 0;
828                 hlend = 0;
829         }
830
831         if (hlend - hlstart < 2) {
832                 hlend = hlstart + 2;
833         }
834         
835         st->txtscroll = *scroll;
836         st->txtscroll.ymax = ar->winy - pix_top_margin - hlstart;
837         st->txtscroll.ymin = ar->winy - pix_top_margin - hlend;
838
839         CLAMP(st->txtscroll.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
840         CLAMP(st->txtscroll.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
841 }
842
843 static void draw_textscroll(SpaceText *st, rcti *scroll, rcti *back)
844 {
845         bTheme *btheme = UI_GetTheme();
846         uiWidgetColors wcol = btheme->tui.wcol_scroll;
847         unsigned char col[4];
848         float rad;
849         
850         UI_ThemeColor(TH_BACK);
851         glRecti(back->xmin, back->ymin, back->xmax, back->ymax);
852
853         uiWidgetScrollDraw(&wcol, scroll, &st->txtbar, (st->flags & ST_SCROLL_SELECT) ? UI_SCROLL_PRESSED : 0);
854
855         uiSetRoundBox(UI_CNR_ALL);
856         rad = 0.4f * min_ii(BLI_rcti_size_x(&st->txtscroll), BLI_rcti_size_y(&st->txtscroll));
857         UI_GetThemeColor3ubv(TH_HILITE, col);
858         col[3] = 48;
859         glColor4ubv(col);
860         glEnable(GL_BLEND);
861         uiRoundBox(st->txtscroll.xmin + 1, st->txtscroll.ymin, st->txtscroll.xmax - 1, st->txtscroll.ymax, rad);
862         glDisable(GL_BLEND);
863 }
864
865 /*********************** draw documentation *******************************/
866
867 static void draw_documentation(SpaceText *st, ARegion *ar)
868 {
869         TextLine *tmp;
870         char *docs, buf[DOC_WIDTH + 1], *p;
871         int i, br, lines;
872         int boxw, boxh, l, x, y /* , top */ /* UNUSED */;
873         
874         if (!st || !st->text) return;
875         if (!texttool_text_is_active(st->text)) return;
876         
877         docs = texttool_docs_get();
878
879         if (!docs) return;
880
881         /* Count the visible lines to the cursor */
882         for (tmp = st->text->curl, l = -st->top; tmp; tmp = tmp->prev, l++) ;
883         if (l < 0) return;
884         
885         if (st->showlinenrs) {
886                 x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET + TEXTXLOC - 4;
887         }
888         else {
889                 x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET - 4;
890         }
891         if (texttool_suggest_first()) {
892                 x += SUGG_LIST_WIDTH * st->cwidth + 50;
893         }
894
895         /* top = */ /* UNUSED */ y = ar->winy - st->lheight_dpi * l - 2;
896         boxw = DOC_WIDTH * st->cwidth + 20;
897         boxh = (DOC_HEIGHT + 1) * (st->lheight_dpi + TXT_LINE_SPACING);
898
899         /* Draw panel */
900         UI_ThemeColor(TH_BACK);
901         glRecti(x, y, x + boxw, y - boxh);
902         UI_ThemeColor(TH_SHADE1);
903         glBegin(GL_LINE_LOOP);
904         glVertex2i(x, y);
905         glVertex2i(x + boxw, y);
906         glVertex2i(x + boxw, y - boxh);
907         glVertex2i(x, y - boxh);
908         glEnd();
909         glBegin(GL_LINE_LOOP);
910         glVertex2i(x + boxw - 10, y - 7);
911         glVertex2i(x + boxw - 4, y - 7);
912         glVertex2i(x + boxw - 7, y - 2);
913         glEnd();
914         glBegin(GL_LINE_LOOP);
915         glVertex2i(x + boxw - 10, y - boxh + 7);
916         glVertex2i(x + boxw - 4, y - boxh + 7);
917         glVertex2i(x + boxw - 7, y - boxh + 2);
918         glEnd();
919         UI_ThemeColor(TH_TEXT);
920
921         i = 0; br = DOC_WIDTH; lines = 0; // XXX -doc_scroll;
922         for (p = docs; *p; p++) {
923                 if (*p == '\r' && *(++p) != '\n') *(--p) = '\n';  /* Fix line endings */
924                 if (*p == ' ' || *p == '\t')
925                         br = i;
926                 else if (*p == '\n') {
927                         buf[i] = '\0';
928                         if (lines >= 0) {
929                                 y -= st->lheight_dpi;
930                                 text_draw(st, buf, 0, 0, 1, x + 4, y - 3, NULL);
931                         }
932                         i = 0; br = DOC_WIDTH; lines++;
933                 }
934                 buf[i++] = *p;
935                 if (i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
936                         buf[br] = '\0';
937                         if (lines >= 0) {
938                                 y -= st->lheight_dpi;
939                                 text_draw(st, buf, 0, 0, 1, x + 4, y - 3, NULL);
940                         }
941                         p -= i - br - 1; /* Rewind pointer to last break */
942                         i = 0; br = DOC_WIDTH; lines++;
943                 }
944                 if (lines >= DOC_HEIGHT) break;
945         }
946
947         if (0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) {
948                 // XXX doc_scroll--;
949                 draw_documentation(st, ar);
950         }
951 }
952
953 /*********************** draw suggestion list *******************************/
954
955 static void draw_suggestion_list(SpaceText *st, ARegion *ar)
956 {
957         SuggItem *item, *first, *last, *sel;
958         TextLine *tmp;
959         char str[SUGG_LIST_WIDTH + 1];
960         int w, boxw = 0, boxh, i, l, x, y, b, *top;
961         const int lheight = st->lheight_dpi + TXT_LINE_SPACING;
962         const int margin_x = 2;
963         
964         if (!st->text) return;
965         if (!texttool_text_is_active(st->text)) return;
966
967         first = texttool_suggest_first();
968         last = texttool_suggest_last();
969
970         if (!first || !last) return;
971
972         text_pop_suggest_list();
973         sel = texttool_suggest_selected();
974         top = texttool_suggest_top();
975
976         /* Count the visible lines to the cursor */
977         for (tmp = st->text->curl, l = -st->top; tmp; tmp = tmp->prev, l++) ;
978         if (l < 0) return;
979         
980         if (st->showlinenrs) {
981                 x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET + TEXTXLOC - 4;
982         }
983         else {
984                 x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET - 4;
985         }
986         /* offset back so the start of the text lines up with the suggestions,
987          * not essential but makes suggestions easier to follow */
988         x -= st->cwidth * (st->text->curc - text_find_identifier_start(st->text->curl->line, st->text->curc));
989         y = ar->winy - lheight * l - 2;
990
991         boxw = SUGG_LIST_WIDTH * st->cwidth + 20;
992         boxh = SUGG_LIST_SIZE * lheight + 8;
993         
994         /* not needed but stands out nicer */
995         uiDrawBoxShadow(220, x, y - boxh, x + boxw, y);
996
997         UI_ThemeColor(TH_SHADE1);
998         glRecti(x - 1, y + 1, x + boxw + 1, y - boxh - 1);
999         UI_ThemeColorShade(TH_BACK, 16);
1000         glRecti(x, y, x + boxw, y - boxh);
1001
1002         /* Set the top 'item' of the visible list */
1003         for (i = 0, item = first; i < *top && item->next; i++, item = item->next) ;
1004
1005         for (i = 0; i < SUGG_LIST_SIZE && item; i++, item = item->next) {
1006
1007                 y -= lheight;
1008
1009                 BLI_strncpy(str, item->name, SUGG_LIST_WIDTH);
1010
1011                 w = BLF_width(mono, str);
1012                 
1013                 if (item == sel) {
1014                         UI_ThemeColor(TH_SHADE2);
1015                         glRecti(x + margin_x, y - 3, x + margin_x + w, y + lheight - 3);
1016                 }
1017                 b = 1; /* b=1 color block, text is default. b=0 no block, color text */
1018                 switch (item->type) {
1019                         case 'k': UI_ThemeColor(TH_SYNTAX_B); b = 0; break;
1020                         case 'm': UI_ThemeColor(TH_TEXT); break;
1021                         case 'f': UI_ThemeColor(TH_SYNTAX_L); break;
1022                         case 'v': UI_ThemeColor(TH_SYNTAX_N); break;
1023                         case '?': UI_ThemeColor(TH_TEXT); b = 0; break;
1024                 }
1025                 if (b) {
1026                         glRecti(x + 8, y + 2, x + 11, y + 5);
1027                         UI_ThemeColor(TH_TEXT);
1028                 }
1029                 text_draw(st, str, 0, 0, 1, x + margin_x, y - 1, NULL);
1030
1031                 if (item == last) break;
1032         }
1033 }
1034
1035 /*********************** draw cursor ************************/
1036
1037 static void draw_cursor(SpaceText *st, ARegion *ar)
1038 {
1039         Text *text = st->text;
1040         int vcurl, vcurc, vsell, vselc, hidden = 0;
1041         int x, y, w, i;
1042         const int lheight = st->lheight_dpi + TXT_LINE_SPACING;
1043
1044         /* Draw the selection */
1045         if (text->curl != text->sell || text->curc != text->selc) {
1046                 int offl, offc;
1047                 /* Convert all to view space character coordinates */
1048                 wrap_offset(st, ar, text->curl, text->curc, &offl, &offc);
1049                 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
1050                 vcurc = text_get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
1051                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1052                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1053                 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1054
1055                 if (vcurc < 0) vcurc = 0;
1056                 if (vselc < 0) vselc = 0, hidden = 1;
1057                 
1058                 UI_ThemeColor(TH_SHADE2);
1059                 x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1060                 y = ar->winy;
1061
1062                 if (vcurl == vsell) {
1063                         y -= vcurl * lheight;
1064                         if (vcurc < vselc)
1065                                 glRecti(x + vcurc * st->cwidth - 1, y, x + vselc * st->cwidth, y - lheight);
1066                         else
1067                                 glRecti(x + vselc * st->cwidth - 1, y, x + vcurc * st->cwidth, y - lheight);
1068                 }
1069                 else {
1070                         int froml, fromc, tol, toc;
1071
1072                         if (vcurl < vsell) {
1073                                 froml = vcurl; tol = vsell;
1074                                 fromc = vcurc; toc = vselc;
1075                         }
1076                         else {
1077                                 froml = vsell; tol = vcurl;
1078                                 fromc = vselc; toc = vcurc;
1079                         }
1080
1081                         y -= froml * lheight;
1082                         glRecti(x + fromc * st->cwidth - 1, y, ar->winx, y - lheight); y -= lheight;
1083                         for (i = froml + 1; i < tol; i++)
1084                                 glRecti(x - 4, y, ar->winx, y - lheight),  y -= lheight;
1085
1086                         glRecti(x - 4, y, x + toc * st->cwidth, y - lheight);  y -= lheight;
1087                 }
1088         }
1089         else {
1090                 int offl, offc;
1091                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1092                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1093                 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1094
1095                 if (vselc < 0) {
1096                         vselc = 0;
1097                         hidden = 1;
1098                 }
1099         }
1100
1101         if (st->line_hlight) {
1102                 int x1, x2, y1, y2;
1103
1104                 if (st->wordwrap) {
1105                         int visible_lines = text_get_visible_lines(st, ar, text->sell->line);
1106                         int offl, offc;
1107
1108                         wrap_offset_in_line(st, ar, text->sell, text->selc, &offl, &offc);
1109
1110                         y1 = ar->winy - 2 - (vsell - offl) * lheight;
1111                         y2 = y1 - (lheight * visible_lines + TXT_LINE_SPACING);
1112                 }
1113                 else {
1114                         y1 = ar->winy - 2 - vsell * lheight;
1115                         y2 = y1 - (lheight + TXT_LINE_SPACING);
1116                 }
1117
1118                 if (!(y1 < 0 || y2 > ar->winy)) { /* check we need to draw */
1119                         x1 = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1120                         x2 = x1 + ar->winx;
1121
1122                         glColor4ub(255, 255, 255, 32);
1123                         
1124                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1125                         glEnable(GL_BLEND);
1126                         glRecti(x1 - 4, y1, x2, y2 + TXT_LINE_SPACING);
1127                         glDisable(GL_BLEND);
1128                 }
1129         }
1130         
1131         if (!hidden) {
1132                 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
1133                 x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1134                 x += vselc * st->cwidth;
1135                 y = ar->winy - vsell * lheight;
1136                 
1137                 if (st->overwrite) {
1138                         char ch = text->sell->line[text->selc];
1139                         
1140                         y += TXT_LINE_SPACING;
1141                         w = st->cwidth;
1142                         if (ch == '\t') w *= st->tabnumber - (vselc + st->left) % st->tabnumber;
1143                         
1144                         UI_ThemeColor(TH_HILITE);
1145                         glRecti(x, y - lheight - 1, x + w, y - lheight + 1);
1146                 }
1147                 else {
1148                         UI_ThemeColor(TH_HILITE);
1149                         glRecti(x - 1, y, x + 1, y - lheight);
1150                 }
1151         }
1152 }
1153
1154 /******************* draw matching brackets *********************/
1155
1156 static void draw_brackets(SpaceText *st, ARegion *ar)
1157 {
1158         TextLine *startl, *endl, *linep;
1159         Text *text = st->text;
1160         int b, fc, find, stack, viewc, viewl, offl, offc, x, y;
1161         int startc, endc, c;
1162         
1163         char ch;
1164
1165         // showsyntax must be on or else the format string will be null
1166         if (!text->curl || !st->showsyntax) return;
1167
1168         startl = text->curl;
1169         startc = text->curc;
1170         b = text_check_bracket(startl->line[startc]);
1171         if (b == 0 && startc > 0) b = text_check_bracket(startl->line[--startc]);
1172         if (b == 0) return;
1173
1174         linep = startl;
1175         c = startc;
1176         fc = txt_utf8_offset_to_index(linep->line, startc);
1177         endl = NULL;
1178         endc = -1;
1179         find = -b;
1180         stack = 0;
1181         
1182         /* Don't highlight backets if syntax HL is off or bracket in string or comment. */
1183         if (!linep->format || linep->format[fc] == FMT_TYPE_STRING || linep->format[fc] == FMT_TYPE_COMMENT)
1184                 return;
1185
1186         if (b > 0) {
1187                 /* opening bracket, search forward for close */
1188                 fc++;
1189                 c += BLI_str_utf8_size_safe(linep->line + c);
1190                 while (linep) {
1191                         while (c < linep->len) {
1192                                 if (linep->format && linep->format[fc] != FMT_TYPE_STRING && linep->format[fc] != FMT_TYPE_COMMENT) {
1193                                         b = text_check_bracket(linep->line[c]);
1194                                         if (b == find) {
1195                                                 if (stack == 0) {
1196                                                         endl = linep;
1197                                                         endc = c;
1198                                                         break;
1199                                                 }
1200                                                 stack--;
1201                                         }
1202                                         else if (b == -find) {
1203                                                 stack++;
1204                                         }
1205                                 }
1206                                 fc++;
1207                                 c += BLI_str_utf8_size_safe(linep->line + c);
1208                         }
1209                         if (endl) break;
1210                         linep = linep->next;
1211                         c = 0;
1212                         fc = 0;
1213                 }
1214         }
1215         else {
1216                 /* closing bracket, search backward for open */
1217                 fc--;
1218                 if (c > 0) c -= linep->line + c - BLI_str_prev_char_utf8(linep->line + c);
1219                 while (linep) {
1220                         while (fc >= 0) {
1221                                 if (linep->format && linep->format[fc] != FMT_TYPE_STRING && linep->format[fc] != FMT_TYPE_COMMENT) {
1222                                         b = text_check_bracket(linep->line[c]);
1223                                         if (b == find) {
1224                                                 if (stack == 0) {
1225                                                         endl = linep;
1226                                                         endc = c;
1227                                                         break;
1228                                                 }
1229                                                 stack--;
1230                                         }
1231                                         else if (b == -find) {
1232                                                 stack++;
1233                                         }
1234                                 }
1235                                 fc--;
1236                                 if (c > 0) c -= linep->line + c - BLI_str_prev_char_utf8(linep->line + c);
1237                         }
1238                         if (endl) break;
1239                         linep = linep->prev;
1240                         if (linep) {
1241                                 if (linep->format) fc = strlen(linep->format) - 1;
1242                                 else fc = -1;
1243                                 if (linep->len) c = BLI_str_prev_char_utf8(linep->line + linep->len) - linep->line;
1244                                 else fc = -1;
1245                         }
1246                 }
1247         }
1248
1249         if (!endl || endc == -1)
1250                 return;
1251
1252         UI_ThemeColor(TH_HILITE);
1253         x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1254         y = ar->winy - st->lheight_dpi;
1255
1256         /* draw opening bracket */
1257         ch = startl->line[startc];
1258         wrap_offset(st, ar, startl, startc, &offl, &offc);
1259         viewc = text_get_char_pos(st, startl->line, startc) - st->left + offc;
1260
1261         if (viewc >= 0) {
1262                 viewl = txt_get_span(text->lines.first, startl) - st->top + offl;
1263
1264                 text_font_draw_character(st, x + viewc * st->cwidth, y - viewl * (st->lheight_dpi + TXT_LINE_SPACING), ch);
1265                 text_font_draw_character(st, x + viewc * st->cwidth + 1, y - viewl * (st->lheight_dpi + TXT_LINE_SPACING), ch);
1266         }
1267
1268         /* draw closing bracket */
1269         ch = endl->line[endc];
1270         wrap_offset(st, ar, endl, endc, &offl, &offc);
1271         viewc = text_get_char_pos(st, endl->line, endc) - st->left + offc;
1272
1273         if (viewc >= 0) {
1274                 viewl = txt_get_span(text->lines.first, endl) - st->top + offl;
1275
1276                 text_font_draw_character(st, x + viewc * st->cwidth, y - viewl * (st->lheight_dpi + TXT_LINE_SPACING), ch);
1277                 text_font_draw_character(st, x + viewc * st->cwidth + 1, y - viewl * (st->lheight_dpi + TXT_LINE_SPACING), ch);
1278         }
1279 }
1280
1281 /*********************** main area drawing *************************/
1282
1283 void draw_text_main(SpaceText *st, ARegion *ar)
1284 {
1285         Text *text = st->text;
1286         TextFormatType *tft;
1287         TextLine *tmp;
1288         rcti scroll, back;
1289         char linenr[12];
1290         int i, x, y, winx, linecount = 0, lineno = 0;
1291         int wraplinecount = 0, wrap_skip = 0;
1292         int margin_column_x;
1293
1294         /* dpi controlled line height and font size */
1295         st->lheight_dpi = (U.widget_unit * st->lheight) / 20;
1296         
1297         if (st->lheight_dpi) st->viewlines = (int)ar->winy / (st->lheight_dpi + TXT_LINE_SPACING);
1298         else st->viewlines = 0;
1299
1300         /* if no text, nothing to do */
1301         if (!text)
1302                 return;
1303         
1304         text_update_drawcache(st, ar);
1305
1306         /* make sure all the positional pointers exist */
1307         if (!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1308                 txt_clean_text(text);
1309         
1310         /* update rects for scroll */
1311         calc_text_rcts(st, ar, &scroll, &back); /* scroll will hold the entire bar size */
1312
1313         /* update syntax formatting if needed */
1314         tft = ED_text_format_get(text);
1315         tmp = text->lines.first;
1316         lineno = 0;
1317         for (i = 0; i < st->top && tmp; i++) {
1318                 if (st->showsyntax && !tmp->format)
1319                         tft->format_line(st, tmp, 0);
1320
1321                 if (st->wordwrap) {
1322                         int lines = text_get_visible_lines_no(st, lineno);
1323
1324                         if (wraplinecount + lines > st->top) {
1325                                 wrap_skip = st->top - wraplinecount;
1326                                 break;
1327                         }
1328                         else {
1329                                 wraplinecount += lines;
1330                                 tmp = tmp->next;
1331                                 linecount++;
1332                         }
1333                 }
1334                 else {
1335                         tmp = tmp->next;
1336                         linecount++;
1337                 }
1338
1339                 lineno++;
1340         }
1341
1342         text_font_begin(st);
1343         st->cwidth = BLF_fixed_width(mono);
1344         st->cwidth = MAX2(st->cwidth, (char)1);
1345
1346         /* draw line numbers background */
1347         if (st->showlinenrs) {
1348                 x = TXT_OFFSET + TEXTXLOC;
1349
1350                 UI_ThemeColor(TH_GRID);
1351                 glRecti((TXT_OFFSET - 12), 0, (TXT_OFFSET - 5) + TEXTXLOC, ar->winy - 2);
1352         }
1353         else {
1354                 st->linenrs_tot = 0; /* not used */
1355                 x = TXT_OFFSET;
1356         }
1357         y = ar->winy - st->lheight_dpi;
1358         winx = ar->winx - TXT_SCROLL_WIDTH;
1359         
1360         /* draw cursor */
1361         draw_cursor(st, ar);
1362
1363         /* draw the text */
1364         UI_ThemeColor(TH_TEXT);
1365
1366         for (i = 0; y > 0 && i < st->viewlines && tmp; i++, tmp = tmp->next) {
1367                 if (st->showsyntax && !tmp->format)
1368                         tft->format_line(st, tmp, 0);
1369
1370                 if (st->showlinenrs && !wrap_skip) {
1371                         /* draw line number */
1372                         if (tmp == text->curl)
1373                                 UI_ThemeColor(TH_HILITE);
1374                         else
1375                                 UI_ThemeColor(TH_TEXT);
1376
1377                         BLI_snprintf(linenr, sizeof(linenr), "%*d", st->linenrs_tot, i + linecount + 1);
1378                         /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */
1379                         text_font_draw(st, TXT_OFFSET - 7, y, linenr);
1380
1381                         UI_ThemeColor(TH_TEXT);
1382                 }
1383
1384                 if (st->wordwrap) {
1385                         /* draw word wrapped text */
1386                         int lines = text_draw_wrapped(st, tmp->line, x, y, winx - x, tmp->format, wrap_skip);
1387                         y -= lines * (st->lheight_dpi + TXT_LINE_SPACING);
1388                 }
1389                 else {
1390                         /* draw unwrapped text */
1391                         text_draw(st, tmp->line, st->left, ar->winx / st->cwidth, 1, x, y, tmp->format);
1392                         y -= st->lheight_dpi + TXT_LINE_SPACING;
1393                 }
1394                 
1395                 wrap_skip = 0;
1396         }
1397         
1398         if (st->flags & ST_SHOW_MARGIN) {
1399                 UI_ThemeColor(TH_HILITE);
1400
1401                 margin_column_x = x + st->cwidth * (st->margin_column - st->left);
1402                 
1403                 if (margin_column_x >= x) {
1404                         glBegin(GL_LINES);
1405                         glVertex2i(margin_column_x, 0);
1406                         glVertex2i(margin_column_x, ar->winy - 2);
1407                         glEnd();
1408                 }
1409         }
1410
1411         /* draw other stuff */
1412         draw_brackets(st, ar);
1413         glTranslatef(GLA_PIXEL_OFS, GLA_PIXEL_OFS, 0.0f); /* XXX scroll requires exact pixel space */
1414         draw_textscroll(st, &scroll, &back);
1415         draw_documentation(st, ar);
1416         draw_suggestion_list(st, ar);
1417         
1418         text_font_end(st);
1419 }
1420
1421 /************************** update ***************************/
1422
1423 void text_update_character_width(SpaceText *st)
1424 {
1425         text_font_begin(st);
1426         st->cwidth = BLF_fixed_width(mono);
1427         st->cwidth = MAX2(st->cwidth, (char)1);
1428         text_font_end(st);
1429 }
1430
1431 /* Moves the view to the cursor location,
1432  * also used to make sure the view isn't outside the file */
1433 void text_scroll_to_cursor(SpaceText *st, ScrArea *sa)
1434 {
1435         Text *text;
1436         ARegion *ar = NULL;
1437         int i, x, winx = 0;
1438
1439         if (ELEM3(NULL, st, st->text, st->text->curl)) return;
1440
1441         text = st->text;
1442
1443         for (ar = sa->regionbase.first; ar; ar = ar->next)
1444                 if (ar->regiontype == RGN_TYPE_WINDOW) {
1445                         winx = ar->winx;
1446                         break;
1447                 }
1448         
1449         winx -= TXT_SCROLL_WIDTH;
1450
1451         text_update_character_width(st);
1452
1453         i = txt_get_span(text->lines.first, text->sell);
1454         if (st->wordwrap) {
1455                 int offl, offc;
1456                 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1457                 i += offl;
1458         }
1459
1460         if (st->top + st->viewlines <= i || st->top > i)
1461                 st->top = i - st->viewlines / 2;
1462         
1463         if (st->wordwrap) {
1464                 st->left = 0;
1465         }
1466         else {
1467                 x = text_draw(st, text->sell->line, st->left, text->selc, 0, 0, 0, NULL);
1468
1469                 if (x == 0 || x > winx)
1470                         st->left = text->curc - 0.5 * winx / st->cwidth;
1471         }
1472
1473         if (st->top < 0) st->top = 0;
1474         if (st->left < 0) st->left = 0;
1475 }
1476
1477 void text_update_cursor_moved(bContext *C)
1478 {
1479         ScrArea *sa = CTX_wm_area(C);
1480         SpaceText *st = CTX_wm_space_text(C);
1481
1482         text_scroll_to_cursor(st, sa);
1483 }