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