4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20 * All rights reserved.
22 * The Original Code is: all of this file.
24 * Contributor(s): none yet.
26 * ***** END GPL LICENSE BLOCK *****
34 #include "MEM_guardedalloc.h"
38 #include "BLI_blenlib.h"
39 #include "BLI_utildefines.h"
41 #include "DNA_text_types.h"
42 #include "DNA_space_types.h"
43 #include "DNA_screen_types.h"
44 #include "DNA_userdef_types.h"
46 #include "BKE_context.h"
47 #include "BKE_suggestions.h"
53 #include "ED_datafiles.h"
54 #include "UI_interface.h"
55 #include "UI_resources.h"
57 #include "text_intern.h"
59 /******************** text font drawing ******************/
61 #define mono blf_mono_font
63 static void text_font_begin(SpaceText *st)
65 BLF_size(mono, st->lheight, 72);
68 static void text_font_end(SpaceText *UNUSED(st))
72 static int text_font_draw(SpaceText *UNUSED(st), int x, int y, char *str)
74 BLF_position(mono, x, y, 0);
75 BLF_draw(mono, str, 65535); /* XXX, use real length */
77 return BLF_width(mono, str);
80 static int text_font_draw_character(SpaceText *st, int x, int y, char c)
86 BLF_position(mono, x, y, 0);
87 BLF_draw(mono, str, 1);
92 int text_font_width(SpaceText *UNUSED(st), char *str)
94 return BLF_width(mono, str);
97 /****************** flatten string **********************/
99 static void flatten_string_append(FlattenString *fs, char c, int accum)
101 if(fs->pos>=fs->len && fs->pos>=sizeof(fs->fixedbuf)-1) {
102 char *nbuf; int *naccum;
103 if(fs->len) fs->len*= 2;
104 else fs->len= sizeof(fs->fixedbuf) * 2;
106 nbuf= MEM_callocN(sizeof(*fs->buf)*fs->len, "fs->buf");
107 naccum= MEM_callocN(sizeof(*fs->accum)*fs->len, "fs->accum");
109 memcpy(nbuf, fs->buf, fs->pos * sizeof(*fs->buf));
110 memcpy(naccum, fs->accum, fs->pos * sizeof(*fs->accum));
112 if(fs->buf != fs->fixedbuf) {
114 MEM_freeN(fs->accum);
122 fs->accum[fs->pos]= accum;
127 int flatten_string(SpaceText *st, FlattenString *fs, char *in)
131 memset(fs, 0, sizeof(FlattenString));
132 fs->buf= fs->fixedbuf;
133 fs->accum= fs->fixedaccum;
135 for(r=0, i=0; *in; r++, in++) {
137 if(fs->pos && *(in-1)=='\t')
139 else if(st->tabnumber > 0)
140 i= st->tabnumber - (fs->pos%st->tabnumber);
143 flatten_string_append(fs, ' ', r);
146 flatten_string_append(fs, *in, r);
152 void flatten_string_free(FlattenString *fs)
154 if(fs->buf != fs->fixedbuf)
156 if(fs->accum != fs->fixedaccum)
157 MEM_freeN(fs->accum);
160 /* Checks the specified source string for a Python built-in function name. This
161 name must start at the beginning of the source string and must be followed by
162 a non-identifier (see text_check_identifier(char)) or null character.
164 If a built-in function is found, the length of the matching name is returned.
165 Otherwise, -1 is returned. */
167 static int find_builtinfunc(char *string)
170 char builtinfuncs[][9] = {"and", "as", "assert", "break", "class", "continue", "def",
171 "del", "elif", "else", "except", "exec", "finally",
172 "for", "from", "global", "if", "import", "in",
173 "is", "lambda", "not", "or", "pass", "print",
174 "raise", "return", "try", "while", "yield", "with"};
176 for(a=0; a < sizeof(builtinfuncs)/sizeof(builtinfuncs[0]); a++) {
179 /* If we hit the end of a keyword... (eg. "def") */
180 if(builtinfuncs[a][i]=='\0') {
181 /* If we still have identifier chars in the source (eg. "definate") */
182 if(text_check_identifier(string[i]))
183 i = -1; /* No match */
184 break; /* Next keyword if no match, otherwise we're done */
186 /* If chars mismatch, move on to next keyword */
188 else if(string[i]!=builtinfuncs[a][i]) {
190 break; /* Break inner loop, start next keyword */
194 if(i>0) break; /* If we have a match, we're done */
199 /* Checks the specified source string for a Python special name. This name must
200 start at the beginning of the source string and must be followed by a non-
201 identifier (see text_check_identifier(char)) or null character.
203 If a special name is found, the length of the matching name is returned.
204 Otherwise, -1 is returned. */
206 static int find_specialvar(char *string)
209 /* Check for "def" */
210 if(string[0]=='d' && string[1]=='e' && string[2]=='f')
212 /* Check for "class" */
213 else if(string[0]=='c' && string[1]=='l' && string[2]=='a' && string[3]=='s' && string[4]=='s')
215 /* If next source char is an identifier (eg. 'i' in "definate") no match */
216 if(i==0 || text_check_identifier(string[i]))
221 static int find_decorator(char *string)
223 if(string[0] == '@') {
225 while(text_check_identifier(string[i])) {
233 static int find_bool(char *string)
236 /* Check for "False" */
237 if(string[0]=='F' && string[1]=='a' && string[2]=='l' && string[3]=='s' && string[4]=='e')
239 /* Check for "True" */
240 else if(string[0]=='T' && string[1]=='r' && string[2]=='u' && string[3]=='e')
242 /* Check for "None" */
243 else if(string[0]=='N' && string[1]=='o' && string[2]=='n' && string[3]=='e')
245 /* If next source char is an identifier (eg. 'i' in "definate") no match */
246 if(i==0 || text_check_identifier(string[i]))
251 /* Ensures the format string for the given line is long enough, reallocating
252 as needed. Allocation is done here, alone, to ensure consistency. */
253 int text_check_format_len(TextLine *line, unsigned int len)
256 if(strlen(line->format) < len) {
257 MEM_freeN(line->format);
258 line->format = MEM_mallocN(len+2, "SyntaxFormat");
259 if(!line->format) return 0;
263 line->format = MEM_mallocN(len+2, "SyntaxFormat");
264 if(!line->format) return 0;
270 /* Formats the specified line. If do_next is set, the process will move on to
271 the succeeding line if it is affected (eg. multiline strings). Format strings
272 may contain any of the following characters:
275 '!' Punctuation and other symbols
278 'v' Special variables (class, def)
279 'b' Built-in names (print, for, etc.)
280 'q' Other text (identifiers, etc.)
281 It is terminated with a null-terminator '\0' followed by a continuation
282 flag indicating whether the line is part of a multi-line string. */
284 static void txt_format_line(SpaceText *st, TextLine *line, int do_next)
287 char *str, *fmt, orig, cont, find, prev = ' ';
290 /* Get continuation from previous line */
291 if(line->prev && line->prev->format != NULL) {
292 fmt= line->prev->format;
293 cont = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
297 /* Get original continuation from this line */
298 if(line->format != NULL) {
300 orig = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
304 flatten_string(st, &fs, line->line);
307 if(!text_check_format_len(line, len)) {
308 flatten_string_free(&fs);
314 /* Handle escape sequences by skipping both \ and next char */
316 *fmt = prev; fmt++; str++;
317 if(*str == '\0') break;
318 *fmt = prev; fmt++; str++;
321 /* Handle continuations */
323 /* Triple strings ("""...""" or '''...''') */
324 if(cont & TXT_TRISTR) {
325 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
326 if(*str==find && *(str+1)==find && *(str+2)==find) {
327 *fmt = 'l'; fmt++; str++;
328 *fmt = 'l'; fmt++; str++;
331 /* Handle other strings */
334 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
335 if(*str == find) cont = 0;
340 /* Not in a string... */
342 /* Deal with comments first */
343 if(prev == '#' || *str == '#')
346 else if(*str == '"' || *str == '\'') {
348 cont = (*str== '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR;
349 if(*(str+1) == find && *(str+2) == find) {
350 *fmt = 'l'; fmt++; str++;
351 *fmt = 'l'; fmt++; str++;
356 /* Whitespace (all ws. has been converted to spaces) */
359 /* Numbers (digits not part of an identifier and periods followed by digits) */
360 else if((prev != 'q' && text_check_digit(*str)) || (*str == '.' && text_check_digit(*(str+1))))
363 else if(prev != 'q' && (i=find_bool(str)) != -1)
366 *fmt = 'n'; fmt++; str++;
374 else if(text_check_delim(*str))
376 /* Identifiers and other text (no previous ws. or delims. so text continues) */
379 /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
381 /* Special vars(v) or built-in keywords(b) */
382 if((i=find_specialvar(str)) != -1)
384 else if((i=find_builtinfunc(str)) != -1)
386 else if((i=find_decorator(str)) != -1)
387 prev = 'v'; /* could have a new color for this */
390 *fmt = prev; fmt++; str++;
404 /* Terminate and add continuation char */
409 //print_format(st, line);
411 /* If continuation has changed and we're allowed, process the next line */
412 if(cont!=orig && do_next && line->next) {
413 txt_format_line(st, line->next, do_next);
416 flatten_string_free(&fs);
420 /* Formats every line of the current text */
421 static void txt_format_text(SpaceText *st)
425 if(!st->text) return;
427 for(linep=st->text->lines.first; linep; linep=linep->next)
428 txt_format_line(st, linep, 0);
432 /* Sets the current drawing color based on the format character specified */
433 static void format_draw_color(char formatchar)
435 switch (formatchar) {
436 case '_': /* Whitespace */
438 case '!': /* Symbols */
439 UI_ThemeColorBlend(TH_TEXT, TH_BACK, 0.5f);
441 case '#': /* Comments */
442 UI_ThemeColor(TH_SYNTAX_C);
444 case 'n': /* Numerals */
445 UI_ThemeColor(TH_SYNTAX_N);
447 case 'l': /* Strings */
448 UI_ThemeColor(TH_SYNTAX_L);
450 case 'v': /* Specials: class, def */
451 UI_ThemeColor(TH_SYNTAX_V);
453 case 'b': /* Keywords: for, print, etc. */
454 UI_ThemeColor(TH_SYNTAX_B);
456 case 'q': /* Other text (identifiers) */
458 UI_ThemeColor(TH_TEXT);
463 /************************** draw text *****************************/
465 /***********************/ /*
469 All word-wrap functions follow the algorithm below to maintain consistency.
470 line The line to wrap (tabs converted to spaces)
471 view_width The maximum number of characters displayable in the region
472 This equals region_width/font_width for the region
473 wrap_chars Characters that allow wrapping. This equals [' ', '\t', '-']
475 def wrap(line, view_width, wrap_chars):
477 draw_end = view_width
480 if pos-draw_start >= view_width:
481 print line[draw_start:draw_end]
482 draw_start = draw_end
483 draw_end += view_width
484 elif c in wrap_chars:
487 print line[draw_start:]
489 */ /***********************/
491 int wrap_width(SpaceText *st, ARegion *ar)
493 int winx= ar->winx - TXT_SCROLL_WIDTH;
496 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
497 max= st->cwidth ? (winx-x)/st->cwidth : 0;
498 return max>8 ? max : 8;
501 /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
502 void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
506 int i, j, start, end, chars, max, chop;
511 if(!st->text) return;
512 if(!st->wordwrap) return;
516 /* Move pointer to first visible line (top) */
517 linep= text->lines.first;
519 while(i>0 && linep) {
520 int lines= text_get_visible_lines(st, ar, linep->line);
522 /* Line before top */
523 if(linep == linein) {
525 /* no visible part of line */
538 max= wrap_width(st, ar);
546 for(i=0, j=0; linep->line[j]!='\0'; j++) {
548 /* Mimic replacement of tabs */
551 chars= st->tabnumber-i%st->tabnumber;
552 if(linep==linein && i<cursin) cursin += chars-1;
560 if(chop && linep==linein && i >= cursin) {
576 else if(ch==' ' || ch=='-') {
579 if(linep==linein && i >= cursin)
585 if(linep==linein) break;
590 void wrap_offset_in_line(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
592 int i, j, start, end, chars, max, chop;
597 if(!st->text) return;
598 if(!st->wordwrap) return;
600 max= wrap_width(st, ar);
607 for(i=0, j=0; linein->line[j]!='\0'; j++) {
609 /* Mimic replacement of tabs */
612 chars= st->tabnumber-i%st->tabnumber;
613 if(i<cursin) cursin += chars-1;
621 if(chop && i >= cursin) {
637 else if(ch==' ' || ch=='-') {
648 int text_get_char_pos(SpaceText *st, char *line, int cur)
652 for(i=0; i<cur && line[i]; i++) {
654 a += st->tabnumber-a%st->tabnumber;
661 static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format, int skip)
664 int basex, i, a, len, start, end, max, lines;
666 len= flatten_string(st, &fs, str);
675 for(i=0; i<len; i++) {
677 /* skip hidden part of line */
685 /* Draw the visible portion of text on the overshot line */
686 for(a=start; a<end; a++) {
687 if(st->showsyntax && format) format_draw_color(format[a]);
688 x += text_font_draw_character(st, x, y, str[a]);
698 else if(str[i]==' ' || str[i]=='-') {
703 /* Draw the remaining text */
704 for(a=start; a<len && y > 0; a++) {
705 if(st->showsyntax && format)
706 format_draw_color(format[a]);
708 x += text_font_draw_character(st, x, y, str[a]);
711 flatten_string_free(&fs);
716 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format)
719 int r=0, w= 0, amount;
723 w= flatten_string(st, &fs, str);
725 flatten_string_free(&fs);
726 return 0; /* String is shorter than shift */
730 acc= fs.accum+cshift;
734 if(st->showsyntax && format) {
736 format = format+cshift;
740 amount= MIN2(amount, maxwidth);
742 for(a = 0; a < amount; a++) {
743 format_draw_color(format[a]);
744 x += text_font_draw_character(st, x, y, in[a]);
750 amount= MIN2(amount, maxwidth);
753 text_font_draw(st, x, y, in);
757 while(w-- && *acc++ < maxwidth)
761 flatten_string_free(&fs);
765 else if(st->showlinenrs)
766 return r+TXT_OFFSET+TEXTXLOC;
771 /************************ cache utilities *****************************/
773 typedef struct DrawCache {
775 int total_lines, nlines;
777 /* this is needed to check cache relevance */
778 int winx, wordwrap, showlinenrs, tabnumber;
781 char text_id[MAX_ID_NAME];
783 /* for partial lines recalculation */
785 int valid_head, valid_tail; /* amount of unchanged lines */
788 static void text_drawcache_init(SpaceText *st)
790 DrawCache *drawcache= MEM_callocN(sizeof (DrawCache), "text draw cache");
793 drawcache->nlines= BLI_countlist(&st->text->lines);
794 drawcache->text_id[0]= '\0';
796 st->drawcache= drawcache;
799 static void text_update_drawcache(SpaceText *st, ARegion *ar)
801 DrawCache *drawcache;
802 int full_update= 0, nlines= 0;
805 if(!st->drawcache) text_drawcache_init(st);
807 text_update_character_width(st);
809 drawcache= (DrawCache *)st->drawcache;
810 nlines= drawcache->nlines;
812 /* check if full cache update is needed */
813 full_update|= drawcache->winx != ar->winx; /* area was resized */
814 full_update|= drawcache->wordwrap != st->wordwrap; /* word-wrapping option was toggled */
815 full_update|= drawcache->showlinenrs != st->showlinenrs; /* word-wrapping option was toggled */
816 full_update|= drawcache->tabnumber != st->tabnumber; /* word-wrapping option was toggled */
817 full_update|= drawcache->lheight != st->lheight; /* word-wrapping option was toggled */
818 full_update|= drawcache->cwidth != st->cwidth; /* word-wrapping option was toggled */
819 full_update|= strncmp(drawcache->text_id, txt->id.name, MAX_ID_NAME); /* text datablock was changed */
822 /* update line heights */
823 if(full_update || !drawcache->line_height) {
824 drawcache->valid_head = 0;
825 drawcache->valid_tail = 0;
826 drawcache->update_flag = 1;
829 if(drawcache->update_flag) {
830 TextLine *line= st->text->lines.first;
831 int lineno= 0, size, lines_count;
832 int *fp= drawcache->line_height, *new_tail, *old_tail;
834 nlines= BLI_countlist(&txt->lines);
835 size= sizeof(int)*nlines;
837 if(fp) fp= MEM_reallocN(fp, size);
838 else fp= MEM_callocN(size, "text drawcache line_height");
840 drawcache->valid_tail= drawcache->valid_head= 0;
841 old_tail= fp + drawcache->nlines - drawcache->valid_tail;
842 new_tail= fp + nlines - drawcache->valid_tail;
843 memmove(new_tail, old_tail, drawcache->valid_tail);
845 drawcache->total_lines= 0;
848 st->linenrs_tot= (int)floor(log10((float)nlines)) + 1;
851 if(drawcache->valid_head) { /* we're inside valid head lines */
852 lines_count= fp[lineno];
853 drawcache->valid_head--;
854 } else if (lineno > new_tail - fp) { /* we-re inside valid tail lines */
855 lines_count= fp[lineno];
857 lines_count= text_get_visible_lines(st, ar, line->line);
860 fp[lineno]= lines_count;
864 drawcache->total_lines+= lines_count;
867 drawcache->line_height= fp;
870 if(drawcache->line_height) {
871 MEM_freeN(drawcache->line_height);
872 drawcache->line_height= NULL;
875 if(full_update || drawcache->update_flag) {
876 nlines= BLI_countlist(&txt->lines);
879 st->linenrs_tot= (int)floor(log10((float)nlines)) + 1;
882 drawcache->total_lines= nlines;
885 drawcache->nlines= nlines;
888 drawcache->winx = ar->winx;
889 drawcache->wordwrap = st->wordwrap;
890 drawcache->lheight = st->lheight;
891 drawcache->cwidth = st->cwidth;
892 drawcache->showlinenrs = st->showlinenrs;
893 drawcache->tabnumber = st->tabnumber;
895 strncpy(drawcache->text_id, txt->id.name, MAX_ID_NAME);
897 /* clear update flag */
898 drawcache->update_flag = 0;
899 drawcache->valid_head = 0;
900 drawcache->valid_tail = 0;
903 void text_drawcache_tag_update(SpaceText *st, int full)
905 DrawCache *drawcache= (DrawCache *)st->drawcache;
910 if(drawcache->update_flag) {
911 /* happens when tagging update from space listener */
912 /* should do nothing to prevent locally tagged cache be fully recalculated */
917 int sellno= BLI_findindex(&txt->lines, txt->sell);
918 int curlno= BLI_findindex(&txt->lines, txt->curl);
920 if(curlno < sellno) {
921 drawcache->valid_head= curlno;
922 drawcache->valid_tail= drawcache->nlines - sellno - 1;
924 drawcache->valid_head= sellno;
925 drawcache->valid_tail= drawcache->nlines - curlno - 1;
928 /* quick cache recalculation is also used in delete operator,
929 which could merge lines which are adjusent to current selection lines
930 expand recalculate area to this lines */
931 if(drawcache->valid_head>0) drawcache->valid_head--;
932 if(drawcache->valid_tail>0) drawcache->valid_tail--;
934 drawcache->valid_head= 0;
935 drawcache->valid_tail= 0;
938 drawcache->update_flag= 1;
942 void text_free_caches(SpaceText *st)
944 DrawCache *drawcache= (DrawCache *)st->drawcache;
947 if(drawcache->line_height)
948 MEM_freeN(drawcache->line_height);
950 MEM_freeN(drawcache);
954 /************************ word-wrap utilities *****************************/
956 /* cache should be updated in caller */
957 int text_get_visible_lines_no(SpaceText *st, int lineno)
959 DrawCache *drawcache= (DrawCache *)st->drawcache;
961 return drawcache->line_height[lineno];
964 int text_get_visible_lines(SpaceText *st, ARegion *ar, char *str)
966 int i, j, start, end, max, lines, chars;
969 max= wrap_width(st, ar);
973 for(i= 0, j= 0; str[j] != '\0'; j++) {
974 /* Mimic replacement of tabs */
977 chars= st->tabnumber-i%st->tabnumber;
988 else if(ch==' ' || ch=='-') {
999 int text_get_span_wrap(SpaceText *st, ARegion *ar, TextLine *from, TextLine *to)
1003 TextLine *tmp= from;
1007 if (tmp == to) return ret;
1008 ret+= text_get_visible_lines(st, ar, tmp->line);
1013 } else return txt_get_span(from, to);
1016 int text_get_total_lines(SpaceText *st, ARegion *ar)
1018 DrawCache *drawcache;
1020 text_update_drawcache(st, ar);
1021 drawcache= (DrawCache *)st->drawcache;
1023 return drawcache->total_lines;
1026 /* Move pointer to first visible line (top) */
1027 static TextLine *first_visible_line(SpaceText *st, ARegion *ar, int *wrap_top)
1029 Text *text= st->text;
1030 TextLine* pline= text->lines.first;
1031 int i= st->top, lineno= 0;
1033 text_update_drawcache(st, ar);
1035 if(wrap_top) *wrap_top= 0;
1038 while(i>0 && pline) {
1039 int lines= text_get_visible_lines_no(st, lineno);
1042 if(wrap_top) *wrap_top= i;
1051 for(i=st->top; pline->next && i>0; i--)
1058 /************************ draw scrollbar *****************************/
1060 static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll, rcti *back)
1062 int lhlstart, lhlend, ltexth, sell_off, curl_off;
1063 short barheight, barstart, hlstart, hlend, blank_lines;
1064 short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
1067 pix_bottom_margin = 4;
1068 pix_available = ar->winy - pix_top_margin - pix_bottom_margin;
1069 ltexth= text_get_total_lines(st, ar);
1070 blank_lines = st->viewlines / 2;
1072 /* nicer code: use scroll rect for entire bar */
1073 back->xmin= ar->winx -18;
1074 back->xmax= ar->winx;
1076 back->ymax= ar->winy;
1078 scroll->xmin= ar->winx - 17;
1079 scroll->xmax= ar->winx - 5;
1081 scroll->ymax= 4+pix_available;
1083 /* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */
1084 if(ltexth + blank_lines < st->top + st->viewlines) {
1085 blank_lines = st->top + st->viewlines - ltexth;
1088 ltexth += blank_lines;
1090 barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0;
1092 if(barheight < 20) {
1093 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */
1096 barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0;
1098 st->txtbar= *scroll;
1099 st->txtbar.ymax -= barstart;
1100 st->txtbar.ymin = st->txtbar.ymax - barheight;
1102 CLAMP(st->txtbar.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
1103 CLAMP(st->txtbar.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
1105 st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
1106 if(st->pix_per_line<.1) st->pix_per_line=.1f;
1108 curl_off= text_get_span_wrap(st, ar, st->text->lines.first, st->text->curl);
1109 sell_off= text_get_span_wrap(st, ar, st->text->lines.first, st->text->sell);
1110 lhlstart = MIN2(curl_off, sell_off);
1111 lhlend = MAX2(curl_off, sell_off);
1114 hlstart = (lhlstart * pix_available)/ltexth;
1115 hlend = (lhlend * pix_available)/ltexth;
1117 /* the scrollbar is non-linear sized */
1118 if(pix_bardiff > 0) {
1119 /* the start of the highlight is in the current viewport */
1120 if(ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) {
1121 /* speed the progresion of the start of the highlight through the scrollbar */
1122 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);
1124 else if(lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
1125 /* push hl start down */
1126 hlstart = barstart + barheight;
1128 else if(lhlend > st->top && lhlstart < st->top && hlstart > barstart) {
1133 if(hlend <= hlstart) {
1134 hlend = hlstart + 2;
1137 /* the end of the highlight is in the current viewport */
1138 if(ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) {
1139 /* speed the progresion of the end of the highlight through the scrollbar */
1140 hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines);
1142 else if(lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
1143 /* push hl end up */
1146 else if(lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
1148 hlend = barstart + barheight;
1151 if(hlend <= hlstart) {
1152 hlstart = hlend - 2;
1161 if(hlend - hlstart < 2) {
1162 hlend = hlstart + 2;
1165 st->txtscroll= *scroll;
1166 st->txtscroll.ymax= ar->winy - pix_top_margin - hlstart;
1167 st->txtscroll.ymin= ar->winy - pix_top_margin - hlend;
1169 CLAMP(st->txtscroll.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
1170 CLAMP(st->txtscroll.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
1173 static void draw_textscroll(SpaceText *st, rcti *scroll, rcti *back)
1175 bTheme *btheme= U.themes.first;
1176 uiWidgetColors wcol= btheme->tui.wcol_scroll;
1177 unsigned char col[4];
1180 UI_ThemeColor(TH_BACK);
1181 glRecti(back->xmin, back->ymin, back->xmax, back->ymax);
1183 uiWidgetScrollDraw(&wcol, scroll, &st->txtbar, (st->flags & ST_SCROLL_SELECT)?UI_SCROLL_PRESSED:0);
1186 rad= 0.4f*MIN2(st->txtscroll.xmax - st->txtscroll.xmin, st->txtscroll.ymax - st->txtscroll.ymin);
1187 UI_GetThemeColor3ubv(TH_HILITE, col);
1191 uiRoundBox(st->txtscroll.xmin+1, st->txtscroll.ymin, st->txtscroll.xmax-1, st->txtscroll.ymax, rad);
1192 glDisable(GL_BLEND);
1195 /************************** draw markers **************************/
1197 static void draw_markers(SpaceText *st, ARegion *ar)
1199 Text *text= st->text;
1200 TextMarker *marker, *next;
1201 TextLine *top, *line;
1202 int offl, offc, i, x1, x2, y1, y2, x, y;
1205 /* Move pointer to first visible line (top) */
1206 top= first_visible_line(st, ar, NULL);
1207 topi= BLI_findindex(&text->lines, top);
1209 topy= txt_get_span(text->lines.first, top);
1211 for(marker= text->markers.first; marker; marker= next) {
1214 /* invisible line (before top) */
1215 if(marker->lineno<topi) continue;
1217 line= BLI_findlink(&text->lines, marker->lineno);
1219 /* Remove broken markers */
1220 if(marker->end>line->len || marker->start>marker->end) {
1221 BLI_freelinkN(&text->markers, marker);
1225 wrap_offset(st, ar, line, marker->start, &offl, &offc);
1226 y1 = txt_get_span(top, line) - st->top + offl + topy;
1227 x1 = text_get_char_pos(st, line->line, marker->start) - st->left + offc;
1229 wrap_offset(st, ar, line, marker->end, &offl, &offc);
1230 y2 = txt_get_span(top, line) - st->top + offl + topy;
1231 x2 = text_get_char_pos(st, line->line, marker->end) - st->left + offc;
1233 /* invisible part of line (before top, after last visible line) */
1234 if(y2 < 0 || y1 > st->top+st->viewlines) continue;
1236 glColor3ubv(marker->color);
1237 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1241 y -= y1*st->lheight;
1242 glBegin(GL_LINE_LOOP);
1243 glVertex2i(x+x2*st->cwidth+1, y);
1244 glVertex2i(x+x1*st->cwidth-2, y);
1245 glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
1246 glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
1250 y -= y1*st->lheight;
1251 glBegin(GL_LINE_STRIP);
1252 glVertex2i(ar->winx, y);
1253 glVertex2i(x+x1*st->cwidth-2, y);
1254 glVertex2i(x+x1*st->cwidth-2, y-st->lheight);
1255 glVertex2i(ar->winx, y-st->lheight);
1259 for(i=y1+1; i<y2; i++) {
1262 glVertex2i(ar->winx, y);
1263 glVertex2i(x, y-st->lheight);
1264 glVertex2i(ar->winx, y-st->lheight);
1269 glBegin(GL_LINE_STRIP);
1271 glVertex2i(x+x2*st->cwidth+1, y);
1272 glVertex2i(x+x2*st->cwidth+1, y-st->lheight);
1273 glVertex2i(x, y-st->lheight);
1279 /*********************** draw documentation *******************************/
1281 static void draw_documentation(SpaceText *st, ARegion *ar)
1284 char *docs, buf[DOC_WIDTH+1], *p;
1286 int boxw, boxh, l, x, y, top;
1288 if(!st || !st->text) return;
1289 if(!texttool_text_is_active(st->text)) return;
1291 docs = texttool_docs_get();
1295 /* Count the visible lines to the cursor */
1296 for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1299 if(st->showlinenrs) {
1300 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1303 x= st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4;
1305 if(texttool_suggest_first()) {
1306 x += SUGG_LIST_WIDTH*st->cwidth + 50;
1309 top= y= ar->winy - st->lheight*l - 2;
1310 boxw= DOC_WIDTH*st->cwidth + 20;
1311 boxh= (DOC_HEIGHT+1)*st->lheight;
1314 UI_ThemeColor(TH_BACK);
1315 glRecti(x, y, x+boxw, y-boxh);
1316 UI_ThemeColor(TH_SHADE1);
1317 glBegin(GL_LINE_LOOP);
1319 glVertex2i(x+boxw, y);
1320 glVertex2i(x+boxw, y-boxh);
1321 glVertex2i(x, y-boxh);
1323 glBegin(GL_LINE_LOOP);
1324 glVertex2i(x+boxw-10, y-7);
1325 glVertex2i(x+boxw-4, y-7);
1326 glVertex2i(x+boxw-7, y-2);
1328 glBegin(GL_LINE_LOOP);
1329 glVertex2i(x+boxw-10, y-boxh+7);
1330 glVertex2i(x+boxw-4, y-boxh+7);
1331 glVertex2i(x+boxw-7, y-boxh+2);
1333 UI_ThemeColor(TH_TEXT);
1335 i= 0; br= DOC_WIDTH; lines= 0; // XXX -doc_scroll;
1336 for(p=docs; *p; p++) {
1337 if(*p == '\r' && *(++p) != '\n') *(--p)= '\n'; /* Fix line endings */
1338 if(*p == ' ' || *p == '\t')
1340 else if(*p == '\n') {
1344 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1346 i= 0; br= DOC_WIDTH; lines++;
1349 if(i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
1353 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1355 p -= i-br-1; /* Rewind pointer to last break */
1356 i= 0; br= DOC_WIDTH; lines++;
1358 if(lines >= DOC_HEIGHT) break;
1361 if(0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) {
1362 // XXX doc_scroll--;
1363 draw_documentation(st, ar);
1367 /*********************** draw suggestion list *******************************/
1369 static void draw_suggestion_list(SpaceText *st, ARegion *ar)
1371 SuggItem *item, *first, *last, *sel;
1373 char str[SUGG_LIST_WIDTH+1];
1374 int w, boxw=0, boxh, i, l, x, y, b, *top;
1376 if(!st || !st->text) return;
1377 if(!texttool_text_is_active(st->text)) return;
1379 first = texttool_suggest_first();
1380 last = texttool_suggest_last();
1382 if(!first || !last) return;
1384 text_pop_suggest_list();
1385 sel = texttool_suggest_selected();
1386 top = texttool_suggest_top();
1388 /* Count the visible lines to the cursor */
1389 for(tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1392 if(st->showlinenrs) {
1393 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1396 x = st->cwidth*(st->text->curc-st->left) + TXT_OFFSET - 4;
1398 y = ar->winy - st->lheight*l - 2;
1400 boxw = SUGG_LIST_WIDTH*st->cwidth + 20;
1401 boxh = SUGG_LIST_SIZE*st->lheight + 8;
1403 UI_ThemeColor(TH_SHADE1);
1404 glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
1405 UI_ThemeColor(TH_BACK);
1406 glRecti(x, y, x+boxw, y-boxh);
1408 /* Set the top 'item' of the visible list */
1409 for(i=0, item=first; i<*top && item->next; i++, item=item->next);
1411 for(i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) {
1415 strncpy(str, item->name, SUGG_LIST_WIDTH);
1416 str[SUGG_LIST_WIDTH] = '\0';
1418 w = text_font_width(st, str);
1421 UI_ThemeColor(TH_SHADE2);
1422 glRecti(x+16, y-3, x+16+w, y+st->lheight-3);
1424 b=1; /* b=1 color block, text is default. b=0 no block, color text */
1425 switch (item->type) {
1426 case 'k': UI_ThemeColor(TH_SYNTAX_B); b=0; break;
1427 case 'm': UI_ThemeColor(TH_TEXT); break;
1428 case 'f': UI_ThemeColor(TH_SYNTAX_L); break;
1429 case 'v': UI_ThemeColor(TH_SYNTAX_N); break;
1430 case '?': UI_ThemeColor(TH_TEXT); b=0; break;
1433 glRecti(x+8, y+2, x+11, y+5);
1434 UI_ThemeColor(TH_TEXT);
1436 text_draw(st, str, 0, 0, 1, x+16, y-1, NULL);
1438 if(item == last) break;
1442 /*********************** draw cursor ************************/
1444 static void draw_cursor(SpaceText *st, ARegion *ar)
1446 Text *text= st->text;
1447 int vcurl, vcurc, vsell, vselc, hidden=0;
1450 /* Draw the selection */
1451 if(text->curl!=text->sell || text->curc!=text->selc) {
1453 /* Convert all to view space character coordinates */
1454 wrap_offset(st, ar, text->curl, text->curc, &offl, &offc);
1455 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
1456 vcurc = text_get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
1457 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1458 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1459 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1461 if(vcurc<0) vcurc=0;
1462 if(vselc<0) vselc=0, hidden=1;
1464 UI_ThemeColor(TH_SHADE2);
1465 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1469 y -= vcurl*st->lheight;
1471 glRecti(x+vcurc*st->cwidth-1, y, x+vselc*st->cwidth, y-st->lheight);
1473 glRecti(x+vselc*st->cwidth-1, y, x+vcurc*st->cwidth, y-st->lheight);
1476 int froml, fromc, tol, toc;
1479 froml= vcurl; tol= vsell;
1480 fromc= vcurc; toc= vselc;
1483 froml= vsell; tol= vcurl;
1484 fromc= vselc; toc= vcurc;
1487 y -= froml*st->lheight;
1488 glRecti(x+fromc*st->cwidth-1, y, ar->winx, y-st->lheight); y-=st->lheight;
1489 for(i=froml+1; i<tol; i++)
1490 glRecti(x-4, y, ar->winx, y-st->lheight), y-=st->lheight;
1492 glRecti(x-4, y, x+toc*st->cwidth, y-st->lheight); y-=st->lheight;
1497 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1498 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
1499 vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
1507 if(st->line_hlight) {
1511 int visible_lines = text_get_visible_lines(st, ar, text->sell->line);
1514 wrap_offset_in_line(st, ar, text->sell, text->selc, &offl, &offc);
1516 y1= ar->winy-2 - (vsell-offl)*st->lheight;
1517 y2= y1-st->lheight*visible_lines+1;
1519 y1= ar->winy-2 - vsell*st->lheight;
1520 y2= y1-st->lheight+1;
1523 if(!(y1<0 || y2 > ar->winy)) { /* check we need to draw */
1524 x1= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1527 glColor4ub(255, 255, 255, 32);
1529 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1531 glRecti(x1-4, y1, x2, y2);
1532 glDisable(GL_BLEND);
1537 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
1538 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1539 x += vselc*st->cwidth;
1540 y= ar->winy-2 - vsell*st->lheight;
1543 char ch= text->sell->line[text->selc];
1546 if(ch=='\t') w*= st->tabnumber-(vselc+st->left)%st->tabnumber;
1548 UI_ThemeColor(TH_HILITE);
1549 glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1);
1552 UI_ThemeColor(TH_HILITE);
1553 glRecti(x-1, y, x+1, y-st->lheight);
1558 /******************* draw matching brackets *********************/
1560 static void draw_brackets(SpaceText *st, ARegion *ar)
1562 TextLine *startl, *endl, *linep;
1563 Text *text = st->text;
1564 int b, c, startc, endc, find, stack;
1565 int viewc, viewl, offl, offc, x, y;
1568 // showsyntax must be on or else the format string will be null
1569 if(!text->curl || !st->showsyntax) return;
1573 b= text_check_bracket(startl->line[startc]);
1574 if(b==0 && startc>0) b = text_check_bracket(startl->line[--startc]);
1584 /* Dont highlight backets if syntax HL is off or bracket in string or comment. */
1585 if(!linep->format || linep->format[c] == 'l' || linep->format[c] == '#')
1589 /* opening bracket, search forward for close */
1592 while(c<linep->len) {
1593 if(linep->format && linep->format[c] != 'l' && linep->format[c] != '#') {
1594 b= text_check_bracket(linep->line[c]);
1615 /* closing bracket, search backward for open */
1619 if(linep->format && linep->format[c] != 'l' && linep->format[c] != '#') {
1620 b= text_check_bracket(linep->line[c]);
1637 if(linep) c= linep->len-1;
1641 if(!endl || endc==-1)
1644 UI_ThemeColor(TH_HILITE);
1645 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1646 y= ar->winy - st->lheight;
1648 /* draw opening bracket */
1649 ch= startl->line[startc];
1650 wrap_offset(st, ar, startl, startc, &offl, &offc);
1651 viewc= text_get_char_pos(st, startl->line, startc) - st->left + offc;
1654 viewl= txt_get_span(text->lines.first, startl) - st->top + offl;
1656 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch);
1657 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch);
1660 /* draw closing bracket */
1661 ch= endl->line[endc];
1662 wrap_offset(st, ar, endl, endc, &offl, &offc);
1663 viewc= text_get_char_pos(st, endl->line, endc) - st->left + offc;
1666 viewl= txt_get_span(text->lines.first, endl) - st->top + offl;
1668 text_font_draw_character(st, x+viewc*st->cwidth, y-viewl*st->lheight, ch);
1669 text_font_draw_character(st, x+viewc*st->cwidth+1, y-viewl*st->lheight, ch);
1673 /*********************** main area drawing *************************/
1675 void draw_text_main(SpaceText *st, ARegion *ar)
1677 Text *text= st->text;
1681 int i, x, y, winx, linecount= 0, lineno= 0;
1682 int wraplinecount= 0, wrap_skip= 0;
1684 /* if no text, nothing to do */
1688 text_update_drawcache(st, ar);
1690 /* make sure all the positional pointers exist */
1691 if(!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1692 txt_clean_text(text);
1694 if(st->lheight) st->viewlines= (int)ar->winy/st->lheight;
1695 else st->viewlines= 0;
1697 /* update rects for scroll */
1698 calc_text_rcts(st, ar, &scroll, &back); /* scroll will hold the entire bar size */
1700 /* update syntax formatting if needed */
1701 tmp= text->lines.first;
1703 for(i= 0; i<st->top && tmp; i++) {
1704 if(st->showsyntax && !tmp->format)
1705 txt_format_line(st, tmp, 0);
1708 int lines= text_get_visible_lines_no(st, lineno);
1710 if (wraplinecount+lines>st->top) {
1711 wrap_skip= st->top-wraplinecount;
1714 wraplinecount+= lines;
1726 text_font_begin(st);
1727 st->cwidth= BLF_fixed_width(mono);
1728 st->cwidth= MAX2(st->cwidth, 1);
1730 /* draw line numbers background */
1731 if(st->showlinenrs) {
1732 x= TXT_OFFSET + TEXTXLOC;
1734 UI_ThemeColor(TH_GRID);
1735 glRecti((TXT_OFFSET-12), 0, (TXT_OFFSET-5) + TEXTXLOC, ar->winy - 2);
1738 st->linenrs_tot= 0; /* not used */
1741 y= ar->winy-st->lheight;
1742 winx= ar->winx - TXT_SCROLL_WIDTH;
1745 draw_cursor(st, ar);
1748 UI_ThemeColor(TH_TEXT);
1750 for(i=0; y>0 && i<st->viewlines && tmp; i++, tmp= tmp->next) {
1751 if(st->showsyntax && !tmp->format)
1752 txt_format_line(st, tmp, 0);
1754 if(st->showlinenrs && !wrap_skip) {
1755 /* draw line number */
1756 if(tmp == text->curl)
1757 UI_ThemeColor(TH_HILITE);
1759 UI_ThemeColor(TH_TEXT);
1761 sprintf(linenr, "%d", i + linecount + 1);
1762 /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */
1763 text_font_draw(st, TXT_OFFSET - 7, y, linenr);
1765 UI_ThemeColor(TH_TEXT);
1769 /* draw word wrapped text */
1770 int lines = text_draw_wrapped(st, tmp->line, x, y, winx-x, tmp->format, wrap_skip);
1771 y -= lines*st->lheight;
1774 /* draw unwrapped text */
1775 text_draw(st, tmp->line, st->left, ar->winx/st->cwidth, 1, x, y, tmp->format);
1782 /* draw other stuff */
1783 draw_brackets(st, ar);
1784 draw_markers(st, ar);
1785 glTranslatef(0.375f, 0.375f, 0.0f); /* XXX scroll requires exact pixel space */
1786 draw_textscroll(st, &scroll, &back);
1787 draw_documentation(st, ar);
1788 draw_suggestion_list(st, ar);
1793 /************************** update ***************************/
1795 void text_update_character_width(SpaceText *st)
1797 text_font_begin(st);
1798 st->cwidth= BLF_fixed_width(mono);
1799 st->cwidth= MAX2(st->cwidth, 1);
1803 /* Moves the view to the cursor location,
1804 also used to make sure the view isnt outside the file */
1805 void text_update_cursor_moved(bContext *C)
1807 ScrArea *sa= CTX_wm_area(C);
1808 SpaceText *st= CTX_wm_space_text(C);
1813 if(ELEM3(NULL, st, st->text, st->text->curl)) return;
1817 for(ar=sa->regionbase.first; ar; ar= ar->next)
1818 if(ar->regiontype==RGN_TYPE_WINDOW)
1821 winx -= TXT_SCROLL_WIDTH;
1823 text_update_character_width(st);
1825 i= txt_get_span(text->lines.first, text->sell);
1828 wrap_offset(st, CTX_wm_region(C), text->sell, text->selc, &offl, &offc);
1832 if(st->top+st->viewlines <= i || st->top > i)
1833 st->top= i - st->viewlines/2;
1839 x= text_draw(st, text->sell->line, st->left, text->selc, 0, 0, 0, NULL);
1842 st->left= text->curc-0.5*winx/st->cwidth;
1845 if(st->top < 0) st->top= 0;
1846 if(st->left <0) st->left= 0;