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