c76ac47ffb71465257512eea7f4a85ff3067dfef
[blender-staging.git] / source / blender / src / drawtext.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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 <stdlib.h>
31 #include <math.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #ifndef _WIN32
42 #include <unistd.h>
43 #else
44 #include <io.h>
45 #include "BLI_winstuff.h"
46 #endif   
47 #include "MEM_guardedalloc.h"
48 #include "PIL_time.h"
49
50 #include "BMF_Api.h"
51
52 #include "BLI_blenlib.h"
53 #include "BLI_arithb.h"
54
55 #include "DNA_text_types.h"
56 #include "DNA_space_types.h"
57 #include "DNA_screen_types.h"
58 #include "DNA_userdef_types.h"
59
60 #include "BKE_utildefines.h"
61 #include "BKE_text.h"
62 #include "BKE_global.h"
63 #include "BKE_main.h"
64 #include "BKE_node.h"
65 #include "BKE_suggestions.h"
66
67 #include "BIF_gl.h"
68 #include "BIF_glutil.h"
69 #include "BIF_keyval.h"
70 #include "BIF_interface.h"
71 #include "BIF_drawtext.h"
72 #include "BIF_editfont.h"
73 #include "BIF_spacetypes.h"
74 #include "BIF_usiblender.h"
75 #include "BIF_screen.h"
76 #include "BIF_toolbox.h"
77 #include "BIF_space.h"
78 #include "BIF_mywindow.h"
79 #include "BIF_resources.h"
80
81 #include "BSE_filesel.h"
82
83 #include "BPY_extern.h"
84
85 #include "mydevice.h"
86 #include "blendef.h" 
87 #include "winlay.h"
88
89 #define TEXTXLOC        38
90
91 #define SUGG_LIST_SIZE 7
92 #define SUGG_LIST_WIDTH 20
93
94 /* forward declarations */
95
96 void drawtextspace(ScrArea *sa, void *spacedata);
97 void winqreadtextspace(struct ScrArea *sa, void *spacedata, struct BWinEvent *evt);
98 void txt_copy_selectbuffer (Text *text);
99 void do_brackets();
100
101 void get_selection_buffer(Text *text);
102 int check_bracket(char *string);
103 static int check_delim(char *string);
104 static int check_numbers(char *string);
105 static int check_builtinfuncs(char *string);
106 static int check_specialvars(char *string);
107 static int check_identifier(char ch);
108
109 static void get_suggest_prefix(Text *text);
110 static void confirm_suggestion(Text *text);
111
112 static void *last_txt_find_string= NULL;
113 static double last_check_time= 0;
114
115 static BMF_Font *spacetext_get_font(SpaceText *st) {
116         static BMF_Font *scr12= NULL;
117         static BMF_Font *scr15= NULL;
118         
119         switch (st->font_id) {
120         default:
121         case 0:
122                 if (!scr12)
123                         scr12= BMF_GetFont(BMF_kScreen12);
124                 return scr12;
125         case 1:
126                 if (!scr15)
127                         scr15= BMF_GetFont(BMF_kScreen15);
128                 return scr15;
129         }
130 }
131
132 static int spacetext_get_fontwidth(SpaceText *st) {
133         return BMF_GetCharacterWidth(spacetext_get_font(st), ' ');
134 }
135
136 static char *temp_char_buf= NULL;
137 static int *temp_char_accum= NULL;
138 static int temp_char_len= 0;
139 static int temp_char_pos= 0;
140
141 static void temp_char_write(char c, int accum) {
142         if (temp_char_len==0 || temp_char_pos>=temp_char_len) {
143                 char *nbuf; int *naccum;
144                 int olen= temp_char_len;
145                 
146                 if (olen) temp_char_len*= 2;
147                 else temp_char_len= 256;
148                 
149                 nbuf= MEM_mallocN(sizeof(*temp_char_buf)*temp_char_len, "temp_char_buf");
150                 naccum= MEM_mallocN(sizeof(*temp_char_accum)*temp_char_len, "temp_char_accum");
151                 
152                 if (olen) {
153                         memcpy(nbuf, temp_char_buf, olen);
154                         memcpy(naccum, temp_char_accum, olen);
155                         
156                         MEM_freeN(temp_char_buf);
157                         MEM_freeN(temp_char_accum);
158                 }
159                 
160                 temp_char_buf= nbuf;
161                 temp_char_accum= naccum;
162         }
163         
164         temp_char_buf[temp_char_pos]= c;        
165         temp_char_accum[temp_char_pos]= accum;
166         
167         if (c==0) temp_char_pos= 0;
168         else temp_char_pos++;
169 }
170
171 void free_txt_data(void) {
172         txt_free_cut_buffer();
173         
174         if (last_txt_find_string) MEM_freeN(last_txt_find_string);
175         if (temp_char_buf) MEM_freeN(temp_char_buf);
176         if (temp_char_accum) MEM_freeN(temp_char_accum);        
177 }
178
179 static int render_string (SpaceText *st, char *in) {
180         int r = 0, i = 0;
181         
182         while(*in) {
183                 if (*in=='\t') {
184                         if (temp_char_pos && *(in-1)=='\t') i= st->tabnumber;
185                         else if (st->tabnumber > 0) i= st->tabnumber - (temp_char_pos%st->tabnumber);
186                         while(i--) temp_char_write(' ', r);
187                 } else temp_char_write(*in, r);
188
189                 r++;
190                 in++;
191         }
192         r= temp_char_pos;
193         temp_char_write(0, 0);
194                 
195         return r;
196 }
197
198 void get_format_string(SpaceText *st) 
199 {
200         Text *text = st->text;
201         TextLine *tmp;
202         char *in_line;
203         char format[2000], check[200], other[2];
204         unsigned char c;
205         int spot, letter, tabs, mem_amount;
206         size_t a, b, len;
207         
208         if(!text) return;
209         tmp = text->lines.first;
210         
211         while(tmp) {
212                 in_line = tmp->line;
213                 
214                 len = strlen(in_line);
215                 /* weak code... but we dont want crashes (ton) */
216                 if(len>2000-1) {
217                         if (tmp->format) MEM_freeN(tmp->format);
218                         tmp->format= NULL;
219                 }
220                 else {
221                         
222                         spot = 0;
223                         tabs = 0;
224                         //see how many tabs we have
225                         for(a = 0; a <len; a++) {
226                                 c = (unsigned char) in_line[a];
227                                 if(c == '\t') {
228                                         tabs++;
229                                 }
230                         }
231                         //calculate the amount of MEM_mallocN we neen
232                         mem_amount = (((tabs*st->tabnumber)-tabs)+2)+len; // +2 for good measure
233                         if (tmp->format) MEM_freeN(tmp->format);
234                         tmp->format = MEM_mallocN(mem_amount, "Syntax_format");
235                         
236                         for (a = 0; a < len; a++) {
237                                 c = (unsigned char) in_line[a];
238
239                                 check[0] = c;
240                                 check[1] = '\0';
241
242                                 if (check_delim(check))
243                                 {
244                                         switch (c) {
245                                                 case '\"':
246                                                         if(in_line[a] == '\"' && in_line[a+1] == '\"' && in_line[a+2] == '\"') { 
247                                                                 format[spot] = format[spot+1] = format[spot+2] = 'l';
248                                                                 spot +=3;
249                                                                 a += 3;
250                                                                 while(in_line[a] != '\"' || in_line[a-1] != '\"' || in_line[a-2] != '\"') {
251                                                                         c = (unsigned char) in_line[a];
252                                                                         if(a >= len) {
253                                                                                 format[spot] = '\0';
254                                                                                 memcpy(tmp->format, format, strlen(format));
255                                                                                 if(!(tmp= tmp->next)) {
256                                                                                         return;
257                                                                                 } else {
258                                                                                         in_line = tmp->line;
259                                                                                         len = strlen(in_line);
260                                                                                         tabs = 0;
261                                                                                         for(b = 0; b <len; b++) {
262                                                                                                 c = (unsigned char) in_line[b];
263                                                                                                 if(c == '\t') {
264                                                                                                         tabs++;
265                                                                                                 }
266                                                                                         }
267                                                                                         mem_amount = (((tabs*st->tabnumber)-tabs)+2)+len;
268                                                                                         if (tmp->format) MEM_freeN(tmp->format);
269                                                                                         tmp->format = MEM_mallocN(mem_amount, "Syntax_format");
270                                                                                         a = 0; spot = 0;
271                                                                                 }
272                                                                         } else {
273                                                                                 if(c == '\t' || c == ' ') {
274                                                                                         if(c == '\t') {
275                                                                                                 for(b = st->tabnumber-(spot%st->tabnumber); b > 0; b--) {
276                                                                                                         format[spot] = ' ';
277                                                                                                         spot++;
278                                                                                                 }
279                                                                                                 a++;
280                                                                                         } else {
281                                                                                                 format[spot] = ' ';
282                                                                                                 a++; spot++;
283                                                                                 }
284                                                                                 } else {
285                                                                                         format[spot] = 'l';
286                                                                                         a++; spot++;
287                                                                                 }
288                                                                         }
289                                                                 }
290                                                                 format[spot] = 'l';
291                                                                 spot++;
292                                                         } else {
293                                                                 format[spot] = 'l';
294                                                                 a++; spot++;
295                                                                 while(in_line[a] != '\"') {
296                                                                         c = (unsigned char) in_line[a];
297                                                                         if(a >= len) {
298                                                                                 format[spot] = '\0';
299                                                                                 memcpy(tmp->format, format, strlen(format));
300                                                                                 if(!(tmp= tmp->next)) {
301                                                                                         return;
302                                                                                 } else {
303                                                                                         in_line = tmp->line;
304                                                                                         len = strlen(in_line);
305                                                                                         for(b = 0; b <len; b++) {
306                                                                                                 c = (unsigned char) in_line[b];
307                                                                                                 if(c == '\t') {
308                                                                                                         tabs++;
309                                                                                                 }
310                                                                                         }
311                                                                                         //calculate the amount of MEM_mallocN we neen
312                                                                                         mem_amount = (((tabs*st->tabnumber)-tabs)+2)+len;
313                                                                                         if (tmp->format) MEM_freeN(tmp->format);
314                                                                                         tmp->format = MEM_mallocN(mem_amount, "Syntax_format");
315                                                                                         a = 0; spot = 0;
316                                                                                 }
317                                                                         }
318                                                                         if(c == '\t' || c == ' ') {
319                                                                                 if(c == '\t') {
320                                                                                         for(b = st->tabnumber-(spot%st->tabnumber); b > 0; b--) {
321                                                                                                 format[spot] = ' ';
322                                                                                                 spot++;
323                                                                                         }
324                                                                                         a++;
325                                                                                 } else {
326                                                                                         format[spot] = ' ';
327                                                                                         a++; spot++;
328                                                                                 }
329                                                                         } else {
330                                                                                 format[spot] = 'l';
331                                                                                 a++; spot++;
332                                                                         }
333                                                                 }
334                                                                 format[spot] = 'l';
335                                                                 spot++;
336                                                         }
337                                                         break;
338                                                 case '\'':
339                                                         if(in_line[a] == '\'' && in_line[a+1] == '\'' && in_line[a+2] == '\'') { 
340                                                                 format[spot] = format[spot+1] = format[spot+2] = 'l';
341                                                                 spot +=3;
342                                                                 a += 3;
343                                                                 while(in_line[a] != '\'' || in_line[a-1] != '\'' || in_line[a-2] != '\'') {
344                                                                         c = (unsigned char) in_line[a];
345                                                                         if(a >= len) {
346                                                                                 format[spot] = '\0';
347                                                                                 memcpy(tmp->format, format, strlen(format));
348                                                                                 if(!(tmp= tmp->next)) {
349                                                                                         return;
350                                                                                 } else {
351                                                                                         in_line = tmp->line;
352                                                                                         len = strlen(in_line);
353                                                                                         tabs = 0;
354                                                                                         for(b = 0; b <len; b++) {
355                                                                                                 c = (unsigned char) in_line[b];
356                                                                                                 if(c == '\t') {
357                                                                                                         tabs++;
358                                                                                                 }
359                                                                                         }
360                                                                                         mem_amount = (((tabs*st->tabnumber)-tabs)+2)+len;
361                                                                                         if (tmp->format) MEM_freeN(tmp->format);
362                                                                                         tmp->format = MEM_mallocN(mem_amount, "Syntax_format");
363                                                                                         a = 0; spot = 0;
364                                                                                 }
365                                                                         } else {
366                                                                                 if(c == '\t' || c == ' ') {
367                                                                                         if(c == '\t') {
368                                                                                                 for(b = st->tabnumber-(spot%st->tabnumber); b > 0; b--) {
369                                                                                                         format[spot] = ' ';
370                                                                                                         spot++;
371                                                                                                 }
372                                                                                                 a++;
373                                                                                         } else {
374                                                                                                 format[spot] = ' ';
375                                                                                                 a++; spot++;
376                                                                                         }
377                                                                                 } else {
378                                                                                         format[spot] = 'l';
379                                                                                         a++; spot++;
380                                                                                 }
381                                                                         }
382                                                                 }
383                                                                 format[spot] = 'l';
384                                                                 spot++;
385                                                         } else {
386                                                                 format[spot] = 'l';
387                                                                 a++; spot++;
388                                                                 while(in_line[a] != '\'') {
389                                                                         c = (unsigned char) in_line[a];
390                                                                         if(a >= len) {
391                                                                                 format[spot] = '\0';
392                                                                                 memcpy(tmp->format, format, strlen(format));
393                                                                                 if(!(tmp= tmp->next)) {
394                                                                                         return;
395                                                                                 } else {
396                                                                                         in_line = tmp->line;
397                                                                                         len = strlen(in_line);
398                                                                                         for(b = 0; b <len; b++) {
399                                                                                                 c = (unsigned char) in_line[b];
400                                                                                                 if(c == '\t') {
401                                                                                                         tabs++;
402                                                                                                 }
403                                                                                         }
404                                                                                         //calculate the amount of MEM_mallocN we neen
405                                                                                         mem_amount = (((tabs*st->tabnumber)-tabs)+2)+len;
406                                                                                         if (tmp->format) MEM_freeN(tmp->format);
407                                                                                         tmp->format = MEM_mallocN(mem_amount, "Syntax_format");
408                                                                                         a = 0; spot = 0;
409                                                                                 }
410                                                                         }
411                                                                         if(c == '\t' || c == ' ') {
412                                                                                 if(c == '\t') {
413                                                                                         for(b = st->tabnumber-(spot%st->tabnumber); b > 0; b--) {
414                                                                                                 format[spot] = ' ';
415                                                                                                 spot++;
416                                                                                         }
417                                                                                         a++;
418                                                                                 } else {
419                                                                                         format[spot] = ' ';
420                                                                                         a++; spot++;
421                                                                                 }
422                                                                         } else {
423                                                                                 format[spot] = 'l';
424                                                                                 a++; spot++;
425                                                                         }
426                                                                 }
427                                                                 format[spot] = 'l';
428                                                                 spot++;
429                                                         }
430                                                         break;
431                                                 case '#':
432                                                         while(a<len) {
433                                                                 c = (unsigned char) in_line[a];
434                                                                 if(c == '\t' || c == ' ') {
435                                                                         if(c == '\t') {
436                                                                                 for(b = st->tabnumber-(spot%st->tabnumber); b > 0; b--) {
437                                                                                         format[spot] = '#';
438                                                                                         spot++;
439                                                                                 }
440                                                                                 a++;
441                                                                         } else {
442                                                                                 format[spot] = '#';
443                                                                                 a++; spot++;
444                                                                         }
445                                                                 } else {
446                                                                         format[spot] = '#';
447                                                                         a++; spot++;
448                                                                 }
449                                                         }
450                                                         break;
451                                                 case ' ':
452                                                         format[spot] = ' ';
453                                                         spot++;
454                                                         break;
455                                                 case '\t':
456                                                         for(b = st->tabnumber-(spot%st->tabnumber); b > 0; b--) {
457                                                                 format[spot] = ' ';
458                                                                 spot++;
459                                                         }
460                                                         break;
461                                                 default:
462                                                         format[spot] = 'q';
463                                                         spot++;
464                                                          
465                                                         break;
466                                         }
467                                 } else if (check_numbers(check)) {
468                                         while (a < len) {
469                                                 c = (unsigned char) in_line[a];
470                                                 other[0] = c;
471                                                 other[1] = '\0';
472                                                 if (check_delim(other) && c != '.') {
473                                                         a--; break;
474                                                 } else {
475                                                         format[spot] = 'n';
476                                                         a++; spot++;
477                                                 }
478                                         }
479                                 } else {
480                                         letter = 0;
481                                         while (a < len) {
482                                                 c = (unsigned char) in_line[a];
483                                                 other[0] = c;
484                                                 other[1] = '\0';
485                                                 if (check_delim(other)) {
486                                                         a--; 
487                                                         break;
488                                                 } else {
489                                                         check[letter] = (unsigned char) in_line[a];
490                                                         letter++; 
491                                                         a++;
492                                                 }
493                                         }
494                                         check[letter] = '\0';
495                                         if (check_builtinfuncs(check)) {
496                                                 for (b = 0; b < strlen(check); b++) {
497                                                         format[spot] = 'b'; 
498                                                         spot++;
499                                                 }
500                                         } else if (check_specialvars(check)) { /*If TRUE then color and color next word*/
501                                                 for (b = 0; b < strlen(check); b++) {
502                                                         format[spot] = 'b';
503                                                         spot++;
504                                                 }
505                                                 a++;
506                                                 format[spot] = 'q';
507                                                 spot++; a++;
508                                                 letter = 0;
509                                                 while (a < len) {
510                                                         c = (unsigned char) in_line[a];
511                                                         other[0] = c;
512                                                         other[1] = '\0';
513                                                         if (check_delim(other)) {
514                                                                 a--; 
515                                                                 break;
516                                                         } else {
517                                                                 check[letter] = (unsigned char) in_line[a];
518                                                                 letter++; 
519                                                                 a++;
520                                                         }
521                                                 }
522                                                 check[letter] = '\0';
523                                                 for (b = 0; b < strlen(check); b++) {
524                                                         format[spot] = 'v';
525                                                         spot++;
526                                                 }
527                                         }else {
528                                                 for (b = 0; b < strlen(check); b++) {
529                                                         format[spot] = 'q';
530                                                         spot++;
531                                                 }
532                                         }
533                                 }
534                         }
535                         format[spot] = '\0';
536                         memcpy(tmp->format, format, strlen(format));
537                 }
538                 
539                 tmp = tmp->next;
540         }
541 }
542
543 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format) {
544         int r=0, w= 0;
545         char *in;
546         int *acc;
547
548         w= render_string(st, str);
549         if(w<cshift ) return 0; /* String is shorter than shift */
550         
551         in= temp_char_buf+cshift;
552         acc= temp_char_accum+cshift;
553         w= w-cshift;
554
555         if (draw) {
556                 if(st->showsyntax && format) {
557                         int amount, a;
558                         char out[2];
559                         format = format+cshift;
560                 
561                         amount = strlen(in);
562                         
563                         for(a = 0; a < amount; a++) {
564                                 out[0] = (unsigned char) in[a]; 
565                                 out[1] = '\0';
566                                 switch (format[a]) {
567                                         case 'l':
568                                                 BIF_ThemeColor(TH_SYNTAX_L);
569                                                 break;
570                                         case 'b':
571                                                 BIF_ThemeColor(TH_SYNTAX_B);
572                                                 break;
573                                         case '#':
574                                                 BIF_ThemeColor(TH_SYNTAX_C);
575                                                 break;
576                                         case 'v': 
577                                                 BIF_ThemeColor(TH_SYNTAX_V);
578                                                 break;
579                                         case 'n':
580                                                 BIF_ThemeColor(TH_SYNTAX_N);
581                                                 break;
582                                         case 'q':
583                                                 BIF_ThemeColor(TH_TEXT);
584                                                 break;
585                                         default:
586                                                 BIF_ThemeColor(TH_TEXT);
587                                                 break;
588                                 }
589                                 glRasterPos2i(x, y);
590                                 BMF_DrawString(spacetext_get_font(st), out);
591                                 x = x+BMF_GetStringWidth(spacetext_get_font(st), out);
592                         }
593                 } else {
594                         glRasterPos2i(x, y);
595                         BMF_DrawString(spacetext_get_font(st), in);
596                 }
597         } else {
598                 while (w-- && *acc++ < maxwidth) {
599                         r+= spacetext_get_fontwidth(st);
600                 }
601         }
602
603         if (cshift && r==0) return 0;
604         else if (st->showlinenrs)
605                 return r+TXT_OFFSET+TEXTXLOC;
606         else
607                 return r+TXT_OFFSET;
608 }
609
610 static void set_cursor_to_pos (SpaceText *st, int x, int y, int sel) 
611 {
612         Text *text;
613         TextLine **linep;
614         int *charp;
615         int w;
616         
617         text= st->text;
618
619         if(sel) { linep= &text->sell; charp= &text->selc; } 
620         else { linep= &text->curl; charp= &text->curc; }
621         
622         y= (curarea->winy - y)/st->lheight;
623         
624         y-= txt_get_span(text->lines.first, *linep) - st->top;
625         
626         if (y>0) {
627                 while (y-- != 0) if((*linep)->next) *linep= (*linep)->next;
628         } else if (y<0) {
629                 while (y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
630         }
631
632         if(st->showlinenrs)
633                 x-= TXT_OFFSET+TEXTXLOC;
634         else
635                 x-= TXT_OFFSET;
636
637         if (x<0) x= 0;
638         x = (x/spacetext_get_fontwidth(st)) + st->left;
639         
640         w= render_string(st, (*linep)->line);
641         if(x<w) *charp= temp_char_accum[x];
642         else *charp= (*linep)->len;
643         
644         if(!sel) txt_pop_sel(text);
645 }
646
647 static void draw_cursor(SpaceText *st) {
648         int h, x, i, w;
649         Text *text= st->text;
650         TextLine *linef, *linel;
651         int charf, charl;
652         char ch[2];
653         
654         if (text->curl==text->sell && text->curc==text->selc) {
655                 x= text_draw(st, text->curl->line, st->left, text->curc, 0, 0, 0, NULL);
656
657                 if (x) {
658                         h= txt_get_span(text->lines.first, text->curl) - st->top;
659
660                         if (st->overwrite) {
661                                 ch[0]= (unsigned char) text->curl->line[text->curc];
662                                 if (ch[0]=='\0') ch[0]=' ';
663                                 ch[1]= '\0';
664                                 w= BMF_GetStringWidth(spacetext_get_font(st), ch);
665                                 BIF_ThemeColor(TH_SHADE2);
666                                 glRecti(x, curarea->winy-st->lheight*(h)-2, x+w, curarea->winy-st->lheight*(h+1)-2);
667                                 BIF_ThemeColor(TH_HILITE);
668                                 glRecti(x, curarea->winy-st->lheight*(h+1)-3, x+w, curarea->winy-st->lheight*(h+1)-1);
669                         } else {
670                                 BIF_ThemeColor(TH_HILITE);
671                                 glRecti(x-1, curarea->winy-st->lheight*(h)-2, x+1, curarea->winy-st->lheight*(h+1)-2);
672                         }
673                 }
674         } else {
675                 int span= txt_get_span(text->curl, text->sell);
676                 
677                 if (span<0) {
678                         linef= text->sell;
679                         charf= text->selc;
680                         
681                         linel= text->curl;
682                         charl= text->curc;
683                 } else if (span>0) {
684                         linef= text->curl;
685                         charf= text->curc;
686         
687                         linel= text->sell;              
688                         charl= text->selc;
689                 } else {
690                         linef= linel= text->curl;
691                         
692                         if (text->curc<text->selc) {
693                                 charf= text->curc;
694                                 charl= text->selc;
695                         } else {
696                                 charf= text->selc;
697                                 charl= text->curc;
698                         }
699                 }
700         
701                         /* Walk to the beginning of visible text */
702                 h= txt_get_span(text->lines.first, linef) - st->top;
703                 while (h++<-1 && linef!=linel) linef= linef->next;
704         
705                 x= text_draw(st, linef->line, st->left, charf, 0, 0, 0, NULL);
706
707                 BIF_ThemeColor(TH_SHADE2);
708
709                 if(st->showlinenrs) {
710                         if (!x) x= TXT_OFFSET + TEXTXLOC -4;
711                 } else {
712                         if (!x) x= TXT_OFFSET - 4;
713                 }
714                 
715                 while (linef && linef != linel) {
716                         h= txt_get_span(text->lines.first, linef) - st->top;
717                         if (h>st->viewlines) break;
718                         
719                         glRecti(x, curarea->winy-st->lheight*(h)-2, curarea->winx, curarea->winy-st->lheight*(h+1)-2);
720                         if(st->showlinenrs)
721                                 glRecti(TXT_OFFSET+TEXTXLOC-4, curarea->winy-st->lheight*(h+1)-2, TXT_OFFSET+TEXTXLOC, curarea->winy-st->lheight*(h+2)-2);
722                         else
723                                 glRecti(TXT_OFFSET-4, curarea->winy-st->lheight*(h+1)-2, TXT_OFFSET, curarea->winy-st->lheight*(h+2)-2);
724
725                         if(st->showlinenrs)
726                                 x= TXT_OFFSET + TEXTXLOC;
727                         else
728                                 x= TXT_OFFSET;
729                         
730                         linef= linef->next;
731                 }
732                 
733                 h= txt_get_span(text->lines.first, linef) - st->top;
734
735                 i= text_draw(st, linel->line, st->left, charl, 0, 0, 0, NULL);
736                 if(i) glRecti(x, curarea->winy-st->lheight*(h)-2, i, curarea->winy-st->lheight*(h+1)-2);
737
738         }
739
740         do_brackets();
741         BIF_ThemeColor(TH_TEXT);
742 }
743
744 static void calc_text_rcts(SpaceText *st)
745 {
746         int lhlstart, lhlend, ltexth;
747         short barheight, barstart, hlstart, hlend, blank_lines;
748         short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
749
750         pix_top_margin = 8;
751         pix_bottom_margin = 4;
752         pix_available = curarea->winy - pix_top_margin - pix_bottom_margin;
753         ltexth= txt_get_span(st->text->lines.first, st->text->lines.last);
754         blank_lines = st->viewlines / 2;
755         
756         /* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */
757         if (ltexth + blank_lines < st->top + st->viewlines) {
758                 blank_lines = st->top + st->viewlines - ltexth;
759         }
760         
761         ltexth += blank_lines;
762
763         barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0;
764         pix_bardiff = 0;
765         if (barheight < 20) {
766                 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */      
767                 barheight = 20;
768         }
769         barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0;
770
771         st->txtbar.xmin = 5;
772         st->txtbar.xmax = 17;
773         st->txtbar.ymax = curarea->winy - pix_top_margin - barstart;
774         st->txtbar.ymin = st->txtbar.ymax - barheight;
775
776         CLAMP(st->txtbar.ymin, pix_bottom_margin, curarea->winy - pix_top_margin);
777         CLAMP(st->txtbar.ymax, pix_bottom_margin, curarea->winy - pix_top_margin);
778
779         st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
780         if (st->pix_per_line<.1) st->pix_per_line=.1f;
781
782         lhlstart = MIN2(txt_get_span(st->text->lines.first, st->text->curl), 
783                                 txt_get_span(st->text->lines.first, st->text->sell));
784         lhlend = MAX2(txt_get_span(st->text->lines.first, st->text->curl), 
785                                 txt_get_span(st->text->lines.first, st->text->sell));
786
787         if(ltexth > 0) {
788                 hlstart = (lhlstart * pix_available)/ltexth;
789                 hlend = (lhlend * pix_available)/ltexth;
790
791                 /* the scrollbar is non-linear sized */
792                 if (pix_bardiff > 0) {
793                         /* the start of the highlight is in the current viewport */
794                         if (ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) { 
795                                 /* speed the progresion of the start of the highlight through the scrollbar */
796                                 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);       
797                         }
798                         else if (lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
799                                 /* push hl start down */
800                                 hlstart = barstart + barheight;
801                         }
802                         else if (lhlend > st->top  && lhlstart < st->top && hlstart > barstart) {
803                                 /*fill out start */
804                                 hlstart = barstart;
805                         }
806
807                         if (hlend <= hlstart) { 
808                                 hlend = hlstart + 2;
809                         }
810
811                         /* the end of the highlight is in the current viewport */
812                         if (ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) { 
813                                 /* speed the progresion of the end of the highlight through the scrollbar */
814                                 hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines);    
815                         }
816                         else if (lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
817                                 /* push hl end up */
818                                 hlend = barstart;
819                         }                                       
820                         else if (lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
821                                 /* fill out end */
822                                 hlend = barstart + barheight;
823                         }
824
825                         if (hlend <= hlstart) { 
826                                 hlstart = hlend - 2;
827                         }       
828                 }       
829         }
830         else {
831                 hlstart = 0;
832                 hlend = 0;
833         }
834
835         if (hlend - hlstart < 2) { 
836                 hlend = hlstart + 2;
837         }
838         
839         st->txtscroll.xmin= 5;
840         st->txtscroll.xmax= 17;
841         st->txtscroll.ymax= curarea->winy - pix_top_margin - hlstart;
842         st->txtscroll.ymin= curarea->winy - pix_top_margin - hlend;
843
844         CLAMP(st->txtscroll.ymin, pix_bottom_margin, curarea->winy - pix_top_margin);
845         CLAMP(st->txtscroll.ymax, pix_bottom_margin, curarea->winy - pix_top_margin);
846 }
847
848 static void draw_textscroll(SpaceText *st)
849 {
850         if (!st->text) return;
851
852         calc_text_rcts(st);
853         
854         BIF_ThemeColorShade(TH_SHADE1, -20);
855         glRecti(2, 2, 20, curarea->winy-6);
856         uiEmboss(2, 2, 20, curarea->winy-6, 1);
857
858         BIF_ThemeColor(TH_SHADE1);
859         glRecti(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax);
860
861         BIF_ThemeColor(TH_SHADE2);
862         glRecti(st->txtscroll.xmin, st->txtscroll.ymin, st->txtscroll.xmax, st->txtscroll.ymax);
863
864         uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
865 }
866
867 static void screen_skip(SpaceText *st, int lines)
868 {
869         int last;
870         
871         if (!st) return;
872         if (st->spacetype != SPACE_TEXT) return;
873         if (!st->text) return;
874
875         st->top += lines;
876
877         last= txt_get_span(st->text->lines.first, st->text->lines.last);
878         last= last - (st->viewlines/2);
879         
880         if (st->top>last) st->top= last;
881         if (st->top<0) st->top= 0;
882 }
883
884 /* 
885  * mode 1 == view scroll
886  * mode 2 == scrollbar
887  */
888 static void do_textscroll(SpaceText *st, int mode)
889 {
890         short delta[2]= {0, 0};
891         short mval[2], hold[2], old[2];
892         
893         if (!st->text) return;
894         
895         calc_text_rcts(st);
896
897         st->flags|= ST_SCROLL_SELECT;
898
899         glDrawBuffer(GL_FRONT);
900         uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
901         bglFlush();
902         glDrawBuffer(GL_BACK);
903
904         getmouseco_areawin(mval);
905         old[0]= hold[0]= mval[0];
906         old[1]= hold[1]= mval[1];
907
908         while(get_mbut()&(L_MOUSE|M_MOUSE)) {
909                 getmouseco_areawin(mval);
910
911                 if(old[0]!=mval[0] || old[1]!=mval[1]) {
912                         if (mode==1) {
913                                 delta[0]= (hold[0]-mval[0])/spacetext_get_fontwidth(st);
914                                 delta[1]= (mval[1]-hold[1])/st->lheight;
915                         }
916                         else delta[1]= (hold[1]-mval[1])*st->pix_per_line;
917                         
918                         if (delta[0] || delta[1]) {
919                                 screen_skip(st, delta[1]);
920                                 st->left+= delta[0];
921                                 if (st->left<0) st->left= 0;
922                                 
923                                 scrarea_do_windraw(curarea);
924                                 screen_swapbuffers();
925                                 
926                                 hold[0]=mval[0];
927                                 hold[1]=mval[1];
928                         }
929                         old[0]=mval[0];
930                         old[1]=mval[1];
931                 } else {
932                         BIF_wait_for_statechange();
933                 }
934         }
935         st->flags^= ST_SCROLL_SELECT;
936
937         glDrawBuffer(GL_FRONT);
938         uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
939         bglFlush();
940         glDrawBuffer(GL_BACK);
941 }
942
943 static void do_selection(SpaceText *st, int selecting)
944 {
945         short mval[2], old[2];
946         int sell, selc;
947         int linep2, charp2;
948         int first= 1;
949
950         getmouseco_areawin(mval);
951         old[0]= mval[0];
952         old[1]= mval[1];
953
954         if (!selecting) {
955                 int curl= txt_get_span(st->text->lines.first, st->text->curl);
956                 int curc= st->text->curc;                       
957                 int linep2, charp2;
958                                         
959                 set_cursor_to_pos(st, mval[0], mval[1], 0);
960
961                 linep2= txt_get_span(st->text->lines.first, st->text->curl);
962                 charp2= st->text->selc;
963                                 
964                 if (curl!=linep2 || curc!=charp2)
965                         txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
966         }
967
968         sell= txt_get_span(st->text->lines.first, st->text->sell);
969         selc= st->text->selc;
970
971         while(get_mbut()&L_MOUSE) {
972                 getmouseco_areawin(mval);
973
974                 if (mval[1]<0 || mval[1]>curarea->winy) {
975                         int d= (old[1]-mval[1])*st->pix_per_line;
976                         if (d) screen_skip(st, d);
977
978                         set_cursor_to_pos(st, mval[0], mval[1]<0?0:curarea->winy, 1);
979
980                         scrarea_do_windraw(curarea);
981                         screen_swapbuffers();
982                 } else if (mval[0]<0 || mval[0]>curarea->winx) {
983                         if (mval[0]>curarea->winx) st->left++;
984                         else if (mval[0]<0 && st->left>0) st->left--;
985                         
986                         set_cursor_to_pos(st, mval[0], mval[1], 1);
987                         
988                         scrarea_do_windraw(curarea);
989                         screen_swapbuffers();
990                         
991                         PIL_sleep_ms(10);
992                 } else if (first || old[0]!=mval[0] || old[1]!=mval[1]) {
993                         set_cursor_to_pos(st, mval[0], mval[1], 1);
994
995                         scrarea_do_windraw(curarea);
996                         screen_swapbuffers();
997
998                         old[0]= mval[0];
999                         old[1]= mval[1];
1000                         first= 1;
1001                 } else {
1002                         BIF_wait_for_statechange();
1003                 }
1004         }
1005
1006         linep2= txt_get_span(st->text->lines.first, st->text->sell);
1007         charp2= st->text->selc;
1008                 
1009         if (sell!=linep2 || selc!=charp2)
1010                 txt_undo_add_toop(st->text, UNDO_STO, sell, selc, linep2, charp2);
1011 }
1012
1013 static int do_suggest_select(SpaceText *st)
1014 {
1015         SuggItem *item, *first, *last, *sel;
1016         short mval[2];
1017         TextLine *tmp;
1018         int l, x, y, w, h, i;
1019         int seli, tgti;
1020         
1021         if (!st || !st->text) return 0;
1022         if (!suggest_is_active(st->text)) return 0;
1023
1024         first = suggest_first();
1025         last = suggest_last();
1026         sel = suggest_get_selected();
1027
1028         if (!sel || !last || !first)
1029                 return 0;
1030
1031         /* Count the visible lines to the cursor */
1032         for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1033         if (l<0) return 0;
1034         
1035         if(st->showlinenrs) {
1036                 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1037         } else {
1038                 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1039         }
1040         y = curarea->winy - st->lheight*l - 2;
1041
1042         w = SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 20;
1043         h = SUGG_LIST_SIZE*st->lheight + 8;
1044
1045         getmouseco_areawin(mval);
1046
1047         if (mval[0]<x || x+w<mval[0] || mval[1]<y-h || y<mval[1])
1048                 return 0;
1049
1050         /* Work out which of the visible SUGG_LIST_SIZE items is selected */
1051         for (seli=0, item=sel; seli<3 && item && item!=first; seli++, item=item->prev);
1052
1053         /* Work out the target item index in the visible list */
1054         tgti = (y-mval[1]-4) / st->lheight;
1055         if (tgti<0 || tgti>SUGG_LIST_SIZE)
1056                 return 1;
1057
1058         if (seli<tgti) {
1059                 for (i=seli; i<tgti && sel && sel!=last; i++, sel=sel->next);
1060                 if (sel)
1061                         suggest_set_selected(sel);
1062         } else {
1063                 for (i=seli; i>tgti && sel && sel!=first; i--, sel=sel->prev);
1064                 if (sel)
1065                         suggest_set_selected(sel);
1066         }
1067         return 1;
1068 }
1069
1070 void draw_suggestion_list(SpaceText *st) {
1071         SuggItem *item, *first, *last, *sel;
1072         TextLine *tmp;
1073         char str[SUGG_LIST_WIDTH+1];
1074         int w, boxw=0, boxh, i, l, x, y, b;
1075         
1076         if (!st || !st->text) return;
1077         if (!suggest_is_active(st->text)) return;
1078
1079         first = suggest_first();
1080         last = suggest_last();
1081         sel = suggest_get_selected();
1082
1083         /* Count the visible lines to the cursor */
1084         for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1085         if (l<0) return;
1086         
1087         if(st->showlinenrs) {
1088                 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1089         } else {
1090                 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1091         }
1092         y = curarea->winy - st->lheight*l - 2;
1093
1094         boxw = SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 20;
1095         boxh = SUGG_LIST_SIZE*st->lheight + 8;
1096         
1097         BIF_ThemeColor(TH_SHADE1);
1098         glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
1099         BIF_ThemeColor(TH_BACK);
1100         glRecti(x, y, x+boxw, y-boxh);
1101
1102         for (i=0, item=sel; i<3 && item && item!=first; i++, item=item->prev);
1103
1104         for (i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) {
1105
1106                 y -= st->lheight;
1107
1108                 strncpy(str, item->name, SUGG_LIST_WIDTH);
1109                 str[SUGG_LIST_WIDTH] = '\0';
1110
1111                 w = BMF_GetStringWidth(spacetext_get_font(st), str);
1112                 
1113                 if (item == sel) {
1114                         BIF_ThemeColor(TH_SHADE2);
1115                         glRecti(x+16, y-3, x+16+w, y+st->lheight-3);
1116                 }
1117                 b=1; /* b=1 colour block, text is default. b=0 no block, colour text */
1118                 switch (item->type) {
1119                         case 'k': BIF_ThemeColor(TH_SYNTAX_B); b=0; break;
1120                         case 'm': BIF_ThemeColor(TH_TEXT); break;
1121                         case 'f': BIF_ThemeColor(TH_SYNTAX_L); break;
1122                         case 'v': BIF_ThemeColor(TH_SYNTAX_N); break;
1123                 }
1124                 if (b) {
1125                         glRecti(x+8, y+2, x+11, y+5);
1126                         BIF_ThemeColor(TH_TEXT);
1127                 }
1128                 text_draw(st, str, 0, 0, 1, x+16, y-1, NULL);
1129
1130                 if (item == last) break;
1131         }
1132 }
1133
1134 void drawtextspace(ScrArea *sa, void *spacedata)
1135 {
1136         SpaceText *st= curarea->spacedata.first;
1137         Text *text;
1138         int i;
1139         TextLine *tmp;
1140         char linenr[12];
1141         float col[3];
1142         int linecount = 0;
1143
1144         if (st==NULL || st->spacetype != SPACE_TEXT) return;
1145         
1146         BIF_GetThemeColor3fv(TH_BACK, col);
1147         glClearColor(col[0], col[1], col[2], 0.0);
1148         glClear(GL_COLOR_BUFFER_BIT);
1149         myortho2(-0.375, (float)(sa->winx)-0.375, -0.375, (float)(sa->winy)-0.375);
1150
1151         draw_area_emboss(sa);
1152
1153         text= st->text;
1154         if(!text) return;
1155         
1156         /* Make sure all the positional pointers exist */
1157         if (!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1158                 txt_clean_text(text);
1159         
1160         if(st->lheight) st->viewlines= (int) curarea->winy/st->lheight;
1161         else st->viewlines= 0;
1162         
1163         if(st->showlinenrs) {
1164                 cpack(0x8c787c);
1165                 glRecti(23,  0, (st->lheight==15)?63:59,  curarea->winy - 2);
1166         }
1167
1168         BIF_ThemeColor(TH_TEXT);
1169
1170         draw_cursor(st);
1171
1172         tmp= text->lines.first;
1173         for (i= 0; i<st->top && tmp; i++) {
1174                 tmp= tmp->next;
1175                 linecount++;
1176         }
1177         
1178         if(st->showsyntax) {
1179                 if (tmp && !tmp->format) {
1180                         get_format_string(st);
1181                 }
1182         }
1183         
1184         for (i=0; i<st->viewlines && tmp; i++, tmp= tmp->next) {
1185                 if(st->showlinenrs) {
1186                         /*Change the color of the current line the cursor is on*/
1187                         if(tmp == text->curl) { 
1188                                 BIF_ThemeColor(TH_HILITE);
1189                         } else {
1190                                 BIF_ThemeColor(TH_TEXT);
1191                         }
1192                         if(((float)(i + linecount + 1)/10000.0) < 1.0) {
1193                                 sprintf(linenr, "%4d", i + linecount + 1);
1194                                 glRasterPos2i(TXT_OFFSET - 7, curarea->winy-st->lheight*(i+1));
1195                         } else {
1196                                 sprintf(linenr, "%5d", i + linecount + 1);
1197                                 glRasterPos2i(TXT_OFFSET - 11, curarea->winy-st->lheight*(i+1));
1198                         }
1199                         BIF_ThemeColor(TH_TEXT);
1200                         BMF_DrawString(spacetext_get_font(st), linenr);
1201                         text_draw(st, tmp->line, st->left, 0, 1, TXT_OFFSET + TEXTXLOC, curarea->winy-st->lheight*(i+1), tmp->format);
1202                 } else
1203                         text_draw(st, tmp->line, st->left, 0, 1, TXT_OFFSET, curarea->winy-st->lheight*(i+1), tmp->format);
1204         }
1205
1206         draw_textscroll(st);
1207         draw_suggestion_list(st);
1208
1209         curarea->win_swap= WIN_BACK_OK;
1210 }
1211
1212 /* Moves the view to the cursor location,
1213   also used to make sure the view isnt outside the file */
1214 void pop_space_text (SpaceText *st)
1215 {
1216         int i, x;
1217
1218         if(!st) return;
1219         if(!st->text) return;
1220         if(!st->text->curl) return;
1221                 
1222         i= txt_get_span(st->text->lines.first, st->text->curl);
1223         if (st->top+st->viewlines <= i || st->top > i) {
1224                 st->top= i - st->viewlines/2;
1225         }
1226         
1227         x= text_draw(st, st->text->curl->line, st->left, st->text->curc, 0, 0, 0, NULL);
1228
1229         if (x==0 || x>curarea->winx) {
1230                 st->left= st->text->curc-0.5*(curarea->winx)/spacetext_get_fontwidth(st);
1231         }
1232
1233         if (st->top < 0) st->top= 0;
1234         if (st->left <0) st->left= 0;
1235 }
1236
1237 void add_text_fs(char *file) /* bad but cant pass an as arg here */
1238 {
1239         SpaceText *st= curarea->spacedata.first;
1240         Text *text;
1241
1242         if (st==NULL || st->spacetype != SPACE_TEXT) return;
1243
1244         text= add_text(file);
1245
1246         st->text= text;
1247
1248         st->top= 0;
1249
1250         if (st->showsyntax) get_format_string(st);
1251         allqueue(REDRAWTEXT, 0);
1252         allqueue(REDRAWHEADERS, 0);     
1253 }
1254
1255 void free_textspace(SpaceText *st)
1256 {
1257         if (!st) return;
1258
1259         st->text= NULL;
1260 }
1261
1262 /* returns 0 if file on disk is the same or Text is in memory only
1263    returns 1 if file has been modified on disk since last local edit
1264    returns 2 if file on disk has been deleted
1265    -1 is returned if an error occurs
1266 */
1267 int txt_file_modified(Text *text)
1268 {
1269         struct stat st;
1270         int result;
1271
1272         if (!text || !text->name)
1273                 return 0;
1274
1275         if (!BLI_exists(text->name))
1276                 return 2;
1277
1278         result = stat(text->name, &st);
1279         
1280         if(result == -1)
1281                 return -1;
1282
1283         if((st.st_mode & S_IFMT) != S_IFREG)
1284                 return -1;
1285
1286         if (st.st_mtime > text->mtime)
1287                 return 1;
1288
1289         return 0;
1290 }
1291
1292 static void save_mem_text(char *str)
1293 {
1294         SpaceText *st= curarea->spacedata.first;
1295         Text *text;
1296         
1297         if (!str) return;
1298         
1299         if (!st) return;
1300         if (st->spacetype != SPACE_TEXT) return;
1301
1302         text= st->text;
1303         if(!text) return;
1304         
1305         if (text->name) MEM_freeN(text->name);
1306         text->name= MEM_mallocN(strlen(str)+1, "textname");
1307         strcpy(text->name, str);
1308
1309         text->flags ^= TXT_ISMEM;
1310                 
1311         txt_write_file(text);
1312 }
1313
1314 void txt_write_file(Text *text) 
1315 {
1316         FILE *fp;
1317         TextLine *tmp;
1318         int res;
1319         struct stat st;
1320         
1321         /* Do we need to get a filename? */
1322         if (text->flags & TXT_ISMEM) {
1323                 if (text->name)
1324                         activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", text->name, save_mem_text);
1325                 else
1326                         activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", text->id.name+2, save_mem_text);
1327                 return;
1328         }
1329         
1330         /* Should we ask to save over? */
1331         if (text->flags & TXT_ISTMP) {
1332                 if (BLI_exists(text->name)) {
1333                         if (!okee("Save over")) return;
1334                 } else if (!okee("Create new file")) return;
1335
1336                 text->flags ^= TXT_ISTMP;
1337         }
1338                 
1339         fp= fopen(text->name, "w");
1340         if (fp==NULL) {
1341                 error("Unable to save file");
1342                 return;
1343         }
1344
1345         tmp= text->lines.first;
1346         while (tmp) {
1347                 if (tmp->next) fprintf(fp, "%s\n", tmp->line);
1348                 else fprintf(fp, "%s", tmp->line);
1349                 
1350                 tmp= tmp->next;
1351         }
1352         
1353         fclose (fp);
1354
1355         res= stat(text->name, &st);
1356         text->mtime= st.st_mtime;
1357         
1358         if (text->flags & TXT_ISDIRTY) text->flags ^= TXT_ISDIRTY;
1359 }
1360
1361 void unlink_text(Text *text)
1362 {
1363         bScreen *scr;
1364         ScrArea *area;
1365         SpaceLink *sl;
1366         
1367         /* check if this text was used as script link:
1368          * this check function unsets the pointers and returns how many
1369          * script links used this Text */
1370         if (BPY_check_all_scriptlinks (text)) {
1371                 allqueue(REDRAWBUTSSCRIPT, 0);
1372         }
1373         /* equivalently for pynodes: */
1374         if (nodeDynamicUnlinkText ((ID*)text)) {
1375                 allqueue(REDRAWNODE, 0);
1376         }
1377
1378         for (scr= G.main->screen.first; scr; scr= scr->id.next) {
1379                 for (area= scr->areabase.first; area; area= area->next) {
1380                         for (sl= area->spacedata.first; sl; sl= sl->next) {
1381                                 if (sl->spacetype==SPACE_TEXT) {
1382                                         SpaceText *st= (SpaceText*) sl;
1383                                         
1384                                         if (st->text==text) {
1385                                                 st->text= NULL;
1386                                                 st->top= 0;
1387                                                 
1388                                                 if (st==area->spacedata.first) {
1389                                                         scrarea_queue_redraw(area);
1390                                                 }
1391                                         }
1392                                 }
1393                         }
1394                 }
1395         }
1396 }
1397
1398 int jumptoline_interactive(SpaceText *st) {
1399         short nlines= txt_get_span(st->text->lines.first, st->text->lines.last)+1;
1400         short tmp= txt_get_span(st->text->lines.first, st->text->curl)+1;
1401
1402         if (button(&tmp, 1, nlines, "Jump to line:")) {
1403                 txt_move_toline(st->text, tmp-1, 0);
1404                 pop_space_text(st);
1405                 return 1;
1406         } else {
1407                 return 0;
1408         }
1409 }
1410
1411
1412 int bufferlength;
1413 static char *copybuffer = NULL;
1414
1415 void txt_copy_selectbuffer (Text *text)
1416 {
1417         int length=0;
1418         TextLine *tmp, *linef, *linel;
1419         int charf, charl;
1420         
1421         if (!text) return;
1422         if (!text->curl) return;
1423         if (!text->sell) return;
1424
1425         if (!txt_has_sel(text)) return;
1426         
1427         if (copybuffer) {
1428                 MEM_freeN(copybuffer);
1429                 copybuffer= NULL;
1430         }
1431
1432         if (text->curl==text->sell) {
1433                 linef= linel= text->curl;
1434                 
1435                 if (text->curc < text->selc) {
1436                         charf= text->curc;
1437                         charl= text->selc;
1438                 } else{
1439                         charf= text->selc;
1440                         charl= text->curc;
1441                 }
1442         } else if (txt_get_span(text->curl, text->sell)<0) {
1443                 linef= text->sell;
1444                 linel= text->curl;
1445
1446                 charf= text->selc;              
1447                 charl= text->curc;
1448         } else {
1449                 linef= text->curl;
1450                 linel= text->sell;
1451                 
1452                 charf= text->curc;
1453                 charl= text->selc;
1454         }
1455
1456         if (linef == linel) {
1457                 length= charl-charf;
1458
1459                 copybuffer= MEM_mallocN(length+1, "cut buffera");
1460                 
1461                 BLI_strncpy(copybuffer, linef->line + charf, length+1);
1462         } else {
1463                 length+= linef->len - charf;
1464                 length+= charl;
1465                 length++; /* For the '\n' */
1466                 
1467                 tmp= linef->next;
1468                 while (tmp && tmp!= linel) {
1469                         length+= tmp->len+1;
1470                         tmp= tmp->next;
1471                 }
1472                 
1473                 copybuffer= MEM_mallocN(length+1, "cut bufferb");
1474                 
1475                 strncpy(copybuffer, linef->line+ charf, linef->len-charf);
1476                 length= linef->len-charf;
1477                 
1478                 copybuffer[length++]='\n';
1479                 
1480                 tmp= linef->next;
1481                 while (tmp && tmp!=linel) {
1482                         strncpy(copybuffer+length, tmp->line, tmp->len);
1483                         length+= tmp->len;
1484                         
1485                         copybuffer[length++]='\n';                      
1486                         
1487                         tmp= tmp->next;
1488                 }
1489                 strncpy(copybuffer+length, linel->line, charl);
1490                 length+= charl;
1491                 
1492                 copybuffer[length]=0;
1493         }
1494
1495         bufferlength = length;
1496 }
1497
1498 static char *unixNewLine(char *buffer)
1499 {
1500         char *p, *p2, *output;
1501         
1502         /* we can afford the few extra bytes */
1503         output= MEM_callocN(strlen(buffer)+1, "unixnewline");
1504         for (p= buffer, p2= output; *p; p++)
1505                 if (*p != '\r') *(p2++)= *p;
1506         
1507         *p2= 0;
1508         return(output);
1509 }
1510
1511 static char *winNewLine(char *buffer)
1512 {
1513         char *p, *p2, *output;
1514         int add= 0;
1515         
1516         for (p= buffer; *p; p++)
1517                 if (*p == '\n') add++;
1518                 
1519         bufferlength= p-buffer+add+1;
1520         output= MEM_callocN(bufferlength, "winnewline");
1521         for (p= buffer, p2= output; *p; p++, p2++) {
1522                 if (*p == '\n') { 
1523                         *(p2++)= '\r'; *p2= '\n';
1524                 } else *p2= *p;
1525         }
1526         *p2= 0;
1527         
1528         return(output);
1529 }
1530
1531 void txt_paste_clipboard(Text *text) {
1532
1533         char * buff;
1534         char *temp_buff;
1535         
1536         buff = (char*)getClipboard(0);
1537         if(buff) {
1538                 temp_buff = unixNewLine(buff);
1539                 
1540                 txt_insert_buf(text, temp_buff);
1541                 if(buff){free((void*)buff);}
1542                 if(temp_buff){MEM_freeN(temp_buff);}
1543         }
1544 }
1545
1546 void get_selection_buffer(Text *text)
1547 {
1548         char *buff = getClipboard(1);
1549         txt_insert_buf(text, buff);
1550 }
1551
1552 void txt_copy_clipboard(Text *text) {
1553         char *temp;
1554
1555         txt_copy_selectbuffer(text);
1556
1557         if (copybuffer) {
1558                 copybuffer[bufferlength] = '\0';
1559                 temp = winNewLine(copybuffer);
1560                 
1561                 putClipboard(temp, 0);
1562                 MEM_freeN(temp);
1563                 MEM_freeN(copybuffer);
1564                 copybuffer= NULL;
1565         }
1566 }
1567
1568 /*
1569  * again==0 show find panel or find
1570  * again==1 find text again */
1571 void txt_find_panel(SpaceText *st, int again)
1572 {
1573         Text *text=st->text;
1574         char *findstr= last_txt_find_string;
1575                         
1576         if (again==0) {
1577                 findstr= txt_sel_to_buf(text);
1578         } else if (again==1) {
1579                 char buf[256];
1580
1581         if (findstr && strlen(findstr)<(sizeof(buf)-1))
1582                 strcpy(buf, findstr);
1583         else
1584                 buf[0]= 0;
1585                 
1586         if (sbutton(buf, 0, sizeof(buf)-1, "Find: ") && buf[0])
1587                 findstr= BLI_strdup(buf);
1588         else
1589                 findstr= NULL;
1590         }
1591
1592         if (findstr!=last_txt_find_string) {
1593                 if (last_txt_find_string)
1594                         MEM_freeN(last_txt_find_string);
1595                 last_txt_find_string= findstr;
1596         }
1597                                 
1598         if (findstr) {
1599                 if (txt_find_string(text, findstr))
1600                         pop_space_text(st);
1601                 else
1602                         error("Not found: %s", findstr);
1603         }
1604 }
1605
1606 void run_python_script(SpaceText *st)
1607 {
1608         char *py_filename;
1609         Text *text=st->text;
1610
1611         if (!BPY_txt_do_python_Text(text)) {
1612                 int lineno = BPY_Err_getLinenumber();
1613                 // jump to error if happened in current text:
1614                 py_filename = (char*) BPY_Err_getFilename();
1615
1616                 /* st->text can become NULL: user called Blender.Load(blendfile)
1617                  * before the end of the script. */
1618                 if (!st->text) return;
1619
1620                 if (!strcmp(py_filename, st->text->id.name+2)) {
1621                         error_pyscript(  );
1622                         if (lineno >= 0) {
1623                                 txt_move_toline(text, lineno-1, 0);
1624                                 txt_sel_line(text);
1625                                 pop_space_text(st);
1626                         }       
1627                 } else {
1628                         error("Error in other (possibly external) file, "\
1629                                 "check console");
1630                 }       
1631         }
1632 }
1633
1634 static void set_tabs(Text *text)
1635 {
1636         SpaceText *st = curarea->spacedata.first;
1637         st->currtab_set = setcurr_tab(text);
1638 }
1639
1640 static void get_suggest_prefix(Text *text) {
1641         int i, len;
1642         char *line, tmp[256];
1643
1644         if (!text) return;
1645         if (!suggest_is_active(text)) return;
1646
1647         line= text->curl->line;
1648         for (i=text->curc-1; i>=0; i--)
1649                 if (!check_identifier(line[i]))
1650                         break;
1651         i++;
1652         len= text->curc-i;
1653         if (len > 255) {
1654                 printf("Suggestion prefix too long\n");
1655                 return;
1656         }
1657         strncpy(tmp, line+i, len);
1658         tmp[len]= '\0';
1659         suggest_prefix(tmp);
1660 }
1661
1662 static void confirm_suggestion(Text *text) {
1663         int i, len;
1664         char *line;
1665         SuggItem *sel;
1666
1667         if (!text) return;
1668         if (!suggest_is_active(text)) return;
1669
1670         sel = suggest_get_selected();
1671         if (!sel) return;
1672
1673         line= text->curl->line;
1674         for (i=text->curc-1; i>=0; i--)
1675                 if (!check_identifier(line[i]))
1676                         break;
1677         i++;
1678         len= text->curc-i;
1679         txt_insert_buf(text, sel->name+len);
1680 }
1681
1682 void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
1683 {
1684         unsigned short event= evt->event;
1685         short val= evt->val;
1686         char ascii= evt->ascii;
1687         SpaceText *st= curarea->spacedata.first;
1688         Text *text;
1689         int do_draw=0, p;
1690         int suggesting=0, do_suggest=0; /* 0:just redraw, -1:clear, 1:update prefix */
1691         
1692         if (st==NULL || st->spacetype != SPACE_TEXT) return;
1693         
1694         /* smartass code to prevent the CTRL/ALT events below from not working! */
1695         if(G.qual & (LR_ALTKEY|LR_CTRLKEY))
1696                 if(!ispunct(ascii)) 
1697                         ascii= 0;
1698
1699         text= st->text;
1700         
1701         if (!text) {
1702                 if (event==RIGHTMOUSE) {
1703                         switch (pupmenu("File %t|New %x0|Open... %x1")) {
1704                         case 0:
1705                                 st->text= add_empty_text("Text");
1706                                 st->top= 0;
1707                         
1708                                 allqueue(REDRAWTEXT, 0);
1709                                 allqueue(REDRAWHEADERS, 0);
1710                                 break;
1711                         case 1:
1712                                 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
1713                                 break;
1714                         }
1715                 }
1716                 if (val && !ELEM(G.qual, 0, LR_SHIFTKEY)) {
1717                         if (event==FKEY && G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
1718                                 switch (pupmenu("File %t|New %x0|Open... %x1")) {
1719                                 case 0:
1720                                         st->text= add_empty_text("Text");
1721                                         st->top= 0;
1722                                 
1723                                         allqueue(REDRAWTEXT, 0);
1724                                         allqueue(REDRAWHEADERS, 0);
1725                                         break;
1726                                 case 1:
1727                                         activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
1728                                         break;
1729                                 }
1730                         } 
1731                         else if (event==QKEY) {
1732                                 if (G.qual & LR_CTRLKEY) {
1733                                         if(okee("Quit Blender")) exit_usiblender();
1734                                 }
1735                         }
1736                         else if (event==NKEY) {
1737                                 if (G.qual & LR_ALTKEY) {
1738                                         st->text= add_empty_text("Text");
1739                                         st->top= 0;
1740                                 
1741                                         allqueue(REDRAWTEXT, 0);
1742                                         allqueue(REDRAWHEADERS, 0);
1743                                 }
1744                         }
1745                         else if (event==OKEY) {
1746                                 if (G.qual & LR_ALTKEY) {
1747                                         activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
1748                                 }
1749                         }
1750                 }
1751                 return;
1752         }
1753
1754         suggesting = suggest_is_active(text);
1755         
1756         if (event==LEFTMOUSE) {
1757                 if (val) {
1758                         short mval[2];
1759                         char *buffer;
1760                         set_tabs(text);
1761                         getmouseco_areawin(mval);
1762                         
1763                         if (mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<curarea->winy-2) {
1764                                 do_textscroll(st, 2);
1765                                 do_suggest= -1;
1766                         } else if (do_suggest_select(st)) {
1767                                 do_draw= 1;
1768                                 do_suggest= 0;
1769                         } else {
1770                                 do_selection(st, G.qual&LR_SHIFTKEY);
1771                                 if (txt_has_sel(text)) {
1772                                         buffer = txt_sel_to_buf(text);
1773                                         putClipboard(buffer, 1);
1774                                         MEM_freeN(buffer);
1775                                 }
1776                                 do_draw= 1;
1777                                 do_suggest= -1;
1778                         }
1779                 }
1780         } else if (event==MIDDLEMOUSE) {
1781                 if (val) {
1782                         if (do_suggest_select(st)) {
1783                                 confirm_suggestion(text);
1784                                 do_draw= 1;
1785                                 do_suggest= 0;
1786                         } else if (U.uiflag & USER_MMB_PASTE) {
1787                                 do_selection(st, G.qual&LR_SHIFTKEY);
1788                                 get_selection_buffer(text);
1789                                 do_draw= 1;
1790                         } else {
1791                                 do_textscroll(st, 1);
1792                         }
1793                         do_suggest= -1;
1794                 }
1795         } else if (event==RIGHTMOUSE) {
1796                 if (val) {
1797                         p= pupmenu("File %t|New %x0|Open... %x1|Save %x2|Save As...%x3|Execute Script%x4");
1798
1799                         switch(p) {
1800                                 case 0:
1801                                         st->text= add_empty_text("Text");
1802                                         st->top= 0;
1803                                         
1804                                         allqueue(REDRAWTEXT, 0);
1805                                         allqueue(REDRAWHEADERS, 0);
1806                                         break;
1807
1808                                 case 1:
1809                                         activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
1810                                         break;
1811                                         
1812                                 case 3:
1813                                         text->flags |= TXT_ISMEM;
1814                                         
1815                                 case 2:
1816                                         txt_write_file(text);
1817                                         do_draw= 1;
1818                                         break;
1819                                 case 4:
1820                                         run_python_script(st);
1821                                         do_draw= 1;
1822                                         break;
1823                                 default:
1824                                         break;
1825                         }
1826                         do_suggest= -1;
1827                 }
1828         } else if (ascii) {
1829                 if (text && text->id.lib) {
1830                         error_libdata();
1831
1832                 } else if ((st->overwrite && txt_replace_char(text, ascii)) || txt_add_char(text, ascii)) {
1833                         if (st->showsyntax) get_format_string(st);
1834                         pop_space_text(st);
1835                         do_draw= 1;
1836                         do_suggest= 1;
1837                 }
1838         } else if (val) {
1839                 do_suggest= -1;
1840                 switch (event) {
1841                 case AKEY:
1842                         if (G.qual & LR_ALTKEY) {
1843                                 txt_move_bol(text, G.qual & LR_SHIFTKEY);
1844                                 do_draw= 1;
1845                                 pop_space_text(st);
1846                         } else if (G.qual & LR_CTRLKEY) {
1847                                 txt_sel_all(text);
1848                                 do_draw= 1;
1849                         }
1850                         break; /* BREAK A */
1851                 case CKEY:
1852                         if (G.qual & LR_ALTKEY || G.qual & LR_CTRLKEY) {
1853                                 if(G.qual & LR_SHIFTKEY)
1854                                         txt_copy_clipboard(text);
1855                                 else
1856                                         txt_copy_clipboard(text);
1857
1858                                 do_draw= 1;     
1859                         }
1860                         break; /* BREAK C */
1861                 case DKEY:
1862                         if (text && text->id.lib) {
1863                                 error_libdata();
1864                                 break;
1865                         }
1866                         if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
1867                                 //uncommenting
1868                                 txt_order_cursors(text);
1869                                 uncomment(text);
1870                                 do_draw = 1;
1871                                 if (st->showsyntax) get_format_string(st);
1872                                 break;
1873                         } else if (G.qual == LR_CTRLKEY) {
1874                                 txt_delete_char(text);
1875                                 if (st->showsyntax) get_format_string(st);
1876                                 do_draw= 1;
1877                                 pop_space_text(st);
1878                         }
1879                         break; /* BREAK D */
1880                 case EKEY:
1881                         if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
1882                                 switch(pupmenu("Edit %t|Cut %x0|Copy %x1|Paste %x2|Print Cut Buffer %x3")) {
1883                                 case 0:
1884                                         if (text && text->id.lib) {
1885                                                 error_libdata();
1886                                                 break;
1887                                         }
1888                                         txt_copy_clipboard(text); //First copy to clipboard
1889                                         txt_cut_sel(text);
1890                                         do_draw= 1;
1891                                         break;
1892                                 case 1:
1893                                         txt_copy_clipboard(text);
1894                                         //txt_copy_sel(text);
1895                                         do_draw= 1;
1896                                         break;
1897                                 case 2:
1898                                         if (text && text->id.lib) {
1899                                                 error_libdata();
1900                                                 break;
1901                                         }
1902                                         //txt_paste(text);
1903                                         txt_paste_clipboard(text);
1904                                         if (st->showsyntax) get_format_string(st);
1905                                         do_draw= 1;
1906                                         break;
1907                                 case 3:
1908                                         txt_print_cutbuffer();
1909                                         break;
1910                                 }
1911                         }
1912                         else if (G.qual == LR_CTRLKEY || G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
1913                                 txt_move_eol(text, G.qual & LR_SHIFTKEY);
1914                                 do_draw= 1;
1915                                 pop_space_text(st);
1916                         }
1917                         break; /* BREAK E */
1918                 case FKEY:
1919                         if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
1920                                 switch(pupmenu("File %t|New %x0|Open... %x1|Save %x2|Save As...%x3")) {
1921                                 case 0:
1922                                         st->text= add_empty_text("Text");
1923                                         st->top= 0;
1924                                         
1925                                         allqueue(REDRAWTEXT, 0);
1926                                         allqueue(REDRAWHEADERS, 0);
1927                                         break;
1928                                 case 1:
1929                                         activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
1930                                         break;
1931                                 case 3:
1932                                         text->flags |= TXT_ISMEM;
1933                                 case 2:
1934                                         txt_write_file(text);
1935                                         do_draw= 1;
1936                                         break;
1937                                 }
1938                         }
1939                         else if (G.qual == LR_ALTKEY) {
1940                                 if (txt_has_sel(text)) {
1941                                         txt_find_panel(st,0);
1942                                         do_draw= 1;
1943                                 }
1944                         }
1945                         else if (G.qual == (LR_ALTKEY|LR_CTRLKEY)) {    /* always search button */
1946                                 txt_find_panel(st,1);
1947                                 do_draw= 1;
1948                         }
1949                         break; /* BREAK F */
1950                 case JKEY:
1951                         if (G.qual == LR_ALTKEY) {
1952                                 do_draw= jumptoline_interactive(st);
1953                         }
1954                         break; /* BREAK J */
1955                 case MKEY:
1956                         if (G.qual == LR_ALTKEY) {
1957                                 txt_export_to_object(text);
1958                                 do_draw= 1;     
1959                         }
1960                         break; /* BREAK M */
1961                 case NKEY:
1962                         if (G.qual == LR_ALTKEY) {
1963                                 st->text= add_empty_text("Text");
1964                                 st->top= 0;
1965                         
1966                                 allqueue(REDRAWTEXT, 0);
1967                                 allqueue(REDRAWHEADERS, 0);
1968
1969                         }
1970                         break; /* BREAK N */
1971                 case OKEY:
1972                         if (G.qual == LR_ALTKEY) {
1973                                 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
1974                         }
1975                         break; /* BREAK O */
1976                 case PKEY:
1977                         if (G.qual == LR_ALTKEY) {
1978                                 run_python_script(st);
1979                                 do_draw= 1;
1980                         }
1981                         break; /* BREAK P */
1982                 case QKEY:
1983                         if(okee("Quit Blender")) exit_usiblender();
1984                         break; /* BREAK Q */
1985                 case RKEY:
1986                         if (G.qual == LR_ALTKEY) {
1987                             if (text->compiled) BPY_free_compiled_text(text);
1988                                 text->compiled = NULL;
1989                                 if (okee("Reopen text")) {
1990                                         if (!reopen_text(text))
1991                                                 error("Could not reopen file");
1992                                 if (st->showsyntax) get_format_string(st);
1993                                 }
1994                                 do_draw= 1;     
1995                         }
1996                         break; /* BREAK R */
1997                 case SKEY:
1998                         if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
1999                                 p= pupmenu("Select %t|"
2000                                                         "Select All %x0|"
2001                                                         "Select Line %x1|"
2002                                                         "Jump to Line %x3");
2003                                 switch(p) {
2004                                 case 0:
2005                                         txt_sel_all(text);
2006                                         do_draw= 1;
2007                                         break;
2008                                         
2009                                 case 1:
2010                                         txt_sel_line(text);
2011                                         do_draw= 1;
2012                                         break;
2013                                                                                 
2014                                 case 3:
2015                                         do_draw= jumptoline_interactive(st);
2016                                         break;
2017                                 }
2018                         }
2019                         else if (G.qual & LR_ALTKEY) {
2020                                 /* Event treatment CANNOT enter this if
2021                                 if (G.qual & LR_SHIFTKEY) 
2022                                         if (text) text->flags |= TXT_ISMEM;
2023                                 */
2024                                 txt_write_file(text);
2025                                 do_draw= 1;
2026                         }
2027                         break; /* BREAK S */
2028                 case UKEY:
2029                         //txt_print_undo(text); //debug buffer in console
2030                         if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2031                                 txt_do_redo(text);
2032                                 do_draw= 1;
2033                         }
2034                         if (G.qual == LR_ALTKEY) {
2035                                 txt_do_undo(text);
2036                                 if (st->showsyntax) get_format_string(st);
2037                                 do_draw= 1;
2038                         }
2039                         break; /* BREAK U */
2040                 case VKEY:
2041                         if (G.qual == (LR_ALTKEY| LR_SHIFTKEY)) {
2042                                 switch(pupmenu("View %t|Top of File %x0|Bottom of File %x1|Page Up %x2|Page Down %x3")) {
2043                                 case 0:
2044                                         txt_move_bof(text, 0);
2045                                         do_draw= 1;
2046                                         pop_space_text(st);
2047                                         break;
2048                                 case 1:
2049                                         txt_move_eof(text, 0);
2050                                         do_draw= 1;
2051                                         pop_space_text(st);
2052                                         break;
2053                                 case 2:
2054                                         screen_skip(st, -st->viewlines);
2055                                         do_draw= 1;
2056                                         break;
2057                                 case 3:
2058                                         screen_skip(st, st->viewlines);
2059                                         do_draw= 1;
2060                                         break;
2061                                 }
2062                         }
2063                         /* Support for both Alt-V and Ctrl-V for Paste, for backward compatibility reasons */
2064                         else if (G.qual & LR_ALTKEY || G.qual & LR_CTRLKEY) {
2065                                 if (text && text->id.lib) {
2066                                         error_libdata();
2067                                         break;
2068                                 }
2069                                 /* Throwing in the Shift modifier Paste from the OS clipboard */
2070                                 if (G.qual & LR_SHIFTKEY)
2071                                         txt_paste_clipboard(text);
2072                                 else
2073                                         txt_paste_clipboard(text);
2074                                 if (st->showsyntax) get_format_string(st);
2075                                 do_draw= 1;     
2076                                 pop_space_text(st);
2077                         }
2078                         break; /* BREAK V */
2079                 case XKEY:
2080                         if (G.qual == LR_ALTKEY || G.qual == LR_CTRLKEY) {
2081                                 if (text && text->id.lib) {
2082                                         error_libdata();
2083                                         break;
2084                                 }
2085                                 txt_cut_sel(text);
2086                                 if (st->showsyntax) get_format_string(st);
2087                                 do_draw= 1;     
2088                                 pop_space_text(st);
2089                         }
2090                         break;
2091                 case ZKEY:
2092                         if (G.qual & (LR_ALTKEY|LR_CTRLKEY|LR_COMMANDKEY)) {
2093                                 if (G.qual & LR_SHIFTKEY) {
2094                                         txt_do_redo(text);
2095                                 } else {
2096                                         txt_do_undo(text);
2097                                 }
2098                                 if (st->showsyntax) get_format_string(st);
2099                                 do_draw= 1;
2100                         }
2101                         break;
2102                 case ESCKEY:
2103                         do_suggest= -1;
2104                         break;
2105                 case TABKEY:
2106                         if (text && text->id.lib) {
2107                                 error_libdata();
2108                                 break;
2109                         }
2110                         if (G.qual & LR_SHIFTKEY) {
2111                                 if (txt_has_sel(text)) {
2112                                         txt_order_cursors(text);
2113                                         unindent(text);
2114                                         
2115                                 }
2116                         } else {
2117                                 if ( txt_has_sel(text)) {
2118                                         txt_order_cursors(text);
2119                                         indent(text);
2120                                 } else {
2121                                         txt_add_char(text, '\t');
2122                                 }
2123                         }
2124                         if (st->showsyntax) get_format_string(st);
2125                         pop_space_text(st);
2126                         do_draw= 1;
2127                         st->currtab_set = setcurr_tab(text);
2128                         break;
2129                 case RETKEY:
2130                         if (text && text->id.lib) {
2131                                 error_libdata();
2132                                 break;
2133                         }
2134                         if (suggesting) {
2135                                 confirm_suggestion(text);
2136                                 if (st->showsyntax) get_format_string(st);
2137                                 break;
2138                         }
2139                         //double check tabs before splitting the line
2140                         st->currtab_set = setcurr_tab(text);
2141                         txt_split_curline(text);
2142                         {
2143                                 int a = 0;
2144                                 if (a < st->currtab_set)
2145                                 {
2146                                         while ( a < st->currtab_set) {
2147                                                 txt_add_char(text, '\t');
2148                                                 a++;
2149                                         }
2150                                 }
2151                         }
2152                         if (st->showsyntax) get_format_string(st);
2153                         do_draw= 1;
2154                         pop_space_text(st);
2155                         break;
2156                 case BACKSPACEKEY:
2157                         if (text && text->id.lib) {
2158                                 error_libdata();
2159                                 break;
2160                         }
2161                         if (G.qual & (LR_ALTKEY | LR_CTRLKEY)) {
2162                                 txt_backspace_word(text);
2163                         } else {
2164                                 txt_backspace_char(text);
2165                         }
2166                         set_tabs(text);
2167                         if (st->showsyntax) get_format_string(st);
2168                         do_draw= 1;
2169                         pop_space_text(st);
2170                         do_suggest= 1;
2171                         break;
2172                 case DELKEY:
2173                         if (text && text->id.lib) {
2174                                 error_libdata();
2175                                 break;
2176                         }
2177                         if (G.qual & (LR_ALTKEY | LR_CTRLKEY)) {
2178                                 txt_delete_word(text);
2179                         } else {
2180                                 txt_delete_char(text);
2181                         }
2182                         if (st->showsyntax) get_format_string(st);
2183                         do_draw= 1;
2184                         pop_space_text(st);
2185                         st->currtab_set = setcurr_tab(text);
2186                         break;
2187                 case INSERTKEY:
2188                         st->overwrite= !st->overwrite;
2189                         do_draw= 1;
2190                         do_suggest= 0;
2191                         break;
2192                 case DOWNARROWKEY:
2193                         if (suggesting) {
2194                                 SuggItem *sel = suggest_get_selected();
2195                                 if (sel && sel!=suggest_last() && sel->next)
2196                                         suggest_set_selected(sel->next);
2197                                 do_suggest= 0;
2198                                 break;
2199                         }
2200                         txt_move_down(text, G.qual & LR_SHIFTKEY);
2201                         set_tabs(text);
2202                         do_draw= 1;
2203                         pop_space_text(st);
2204                         break;
2205                 case LEFTARROWKEY:
2206                         if (G.qual & LR_COMMANDKEY)
2207                                 txt_move_bol(text, G.qual & LR_SHIFTKEY);
2208                         else if (G.qual & LR_ALTKEY)
2209                                 txt_jump_left(text, G.qual & LR_SHIFTKEY);
2210                         else
2211                                 txt_move_left(text, G.qual & LR_SHIFTKEY);
2212                         set_tabs(text);
2213                         do_draw= 1;
2214                         pop_space_text(st);
2215                         break;
2216                 case RIGHTARROWKEY:
2217                         if (G.qual & LR_COMMANDKEY)
2218                                 txt_move_eol(text, G.qual & LR_SHIFTKEY);
2219                         else if (G.qual & LR_ALTKEY)
2220                                 txt_jump_right(text, G.qual & LR_SHIFTKEY);
2221                         else
2222                                 txt_move_right(text, G.qual & LR_SHIFTKEY);
2223                         set_tabs(text);
2224                         do_draw= 1;
2225                         pop_space_text(st);
2226                         break;
2227                 case UPARROWKEY:
2228                         if (suggesting) {
2229                                 SuggItem *sel = suggest_get_selected();
2230                                 if (sel && sel!=suggest_first() && sel->prev)
2231                                         suggest_set_selected(sel->prev);
2232                                 do_suggest= 0;
2233                                 break;
2234                         }
2235                         txt_move_up(text, G.qual & LR_SHIFTKEY);
2236                         set_tabs(text);
2237                         do_draw= 1;
2238                         pop_space_text(st);
2239                         break;
2240                 case PAGEDOWNKEY:
2241                         if (suggesting) {
2242                                 int i;
2243                                 SuggItem *sel = suggest_get_selected();
2244                                 for (i=0; i<SUGG_LIST_SIZE-1 && sel && sel!=suggest_last() && sel->next; i++, sel=sel->next)
2245                                         suggest_set_selected(sel->next);
2246                                 do_suggest= 0;
2247                                 break;
2248                         } else {
2249                                 screen_skip(st, st->viewlines);
2250                         }
2251                         do_draw= 1;
2252                         break;
2253                 case PAGEUPKEY:
2254                         if (suggesting) {
2255                                 int i;
2256                                 SuggItem *sel = suggest_get_selected();
2257                                 for (i=0; i<SUGG_LIST_SIZE-1 && sel && sel!=suggest_first() && sel->prev; i++, sel=sel->prev)
2258                                         suggest_set_selected(sel->prev);
2259                                 do_suggest= 0;
2260                                 break;
2261                         } else {
2262                                 screen_skip(st, -st->viewlines);
2263                         }
2264                         do_draw= 1;
2265                         break;
2266                 case HOMEKEY:
2267                         txt_move_bol(text, G.qual & LR_SHIFTKEY);
2268                         do_draw= 1;
2269                         pop_space_text(st);
2270                         break;
2271                 case ENDKEY:
2272                         txt_move_eol(text, G.qual & LR_SHIFTKEY);
2273                         do_draw= 1;
2274                         pop_space_text(st);
2275                         break;
2276                 case WHEELUPMOUSE:
2277                         if (suggesting) {
2278                                 SuggItem *sel = suggest_get_selected();
2279                                 if (sel && sel!=suggest_first() && sel->prev)
2280                                         suggest_set_selected(sel->prev);
2281                                 do_suggest= 0;
2282                         } else {
2283                                 screen_skip(st, -U.wheellinescroll);
2284                         }
2285                         do_draw= 1;
2286                         break;
2287                 case WHEELDOWNMOUSE:
2288                         if (suggesting) {
2289                                 SuggItem *sel = suggest_get_selected();
2290                                 if (sel && sel!=suggest_last() && sel->next)
2291                                         suggest_set_selected(sel->next);
2292                                 do_suggest= 0;
2293                         } else {
2294                                 screen_skip(st, U.wheellinescroll);
2295                         }
2296                         do_draw= 1;
2297                         break;
2298                 default:
2299                         do_suggest= 0;
2300                 }
2301         }
2302
2303         if (last_check_time < PIL_check_seconds_timer() - 1.0) {
2304                 switch (txt_file_modified(text)) {
2305                 case 1:
2306                         /* Modified locally and externally, ahhh. Offer more possibilites. */
2307                         if (text->flags & TXT_ISDIRTY) {
2308                                 switch (pupmenu("External File Modified with Local Changes %t|Load external changes (overwrite local) %x0|Save local changes (overwrite external) %x1|Make text internal %x2")) {
2309                                 case 0:
2310                                         reopen_text(text);
2311                                         if (st->showsyntax) get_format_string(st);
2312                                         do_draw= 1;
2313                                         break;
2314                                 case 1:
2315                                         txt_write_file(text);
2316                                         do_draw= 1;
2317                                         break;
2318                                 case 2:
2319                                         text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
2320                                         MEM_freeN(text->name);
2321                                         text->name= NULL;
2322                                         do_draw= 1;
2323                                         break;
2324                                 }
2325                         } else {
2326                                 switch (pupmenu("External File Modified %t|Reload from disk %x0|Make text internal %x1")) {
2327                                 case 0:
2328                                         reopen_text(text);
2329                                         if (st->showsyntax) get_format_string(st);
2330                                         do_draw= 1;
2331                                         break;
2332                                 case 1:
2333                                         text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
2334                                         MEM_freeN(text->name);
2335                                         text->name= NULL;
2336                                         do_draw= 1;
2337                                         break;
2338                                 }
2339                         }
2340                         break;
2341                 case 2:
2342                         switch (pupmenu("External File Deleted %t|Make text internal %x0")) {
2343                         case 0:
2344                                 text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
2345                                 MEM_freeN(text->name);
2346                                 text->name= NULL;
2347                                 do_draw= 1;
2348                                 break;
2349                         }
2350                         break;
2351                 default:
2352                         last_check_time = PIL_check_seconds_timer();
2353                 }
2354         }
2355
2356         if (suggesting) {
2357                 if (do_suggest == -1) {
2358                         suggest_clear_text();
2359                 } else if (do_suggest == 1) {
2360                         get_suggest_prefix(text);
2361                 }
2362                 do_draw= 1;
2363         }
2364
2365         if (do_draw) {
2366                 ScrArea *sa;
2367                 
2368                 for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
2369                         SpaceText *st= sa->spacedata.first;
2370                         
2371                         if (st && st->spacetype==SPACE_TEXT) {
2372                                 scrarea_queue_redraw(sa);
2373                         }
2374                 }
2375         }
2376 }
2377
2378 void do_brackets(void) 
2379 {
2380         SpaceText *st = curarea->spacedata.first;
2381         Text *text = st->text;
2382         TextLine *tmp, *start;
2383         char test[2];
2384         int d, pos, open, x, y, x2, y2, h=0;
2385         
2386         if(!text) return;
2387         
2388         tmp = text->curl;
2389         start = text->curl;
2390
2391         test[0] = (unsigned char) tmp->line[text->curc];
2392         test[1] = '\0';
2393         
2394         d = check_bracket(test);
2395         if (!d) /*  If not pri char */
2396         {
2397                 test[0] = (unsigned char) tmp->line[text->curc-1];
2398                 test[1] = '\0';
2399                 d = check_bracket(test);
2400                 if(!d) {
2401                         return; /*If the current char or prev is not a bracket then return*/
2402                 } else { /* current char */
2403                         h= txt_get_span(text->lines.first, start) - st->top;
2404                         x = text_draw(st, start->line, st->left, text->curc-1, 0, 0, 0, NULL);
2405                         y = text_draw(st, start->line, st->left, text->curc, 0, 0, 0, NULL);
2406                         if (d < 4) {
2407                                 pos = text->curc;
2408                         } else {
2409                                 pos = text->curc-2;
2410                         }
2411                 }
2412         } else { /* is pri char */
2413                 h= txt_get_span(text->lines.first, start) - st->top;
2414                 x = text_draw(st, start->line, st->left, text->curc, 0, 0, 0, NULL);
2415                 y = text_draw(st, start->line, st->left, text->curc+1, 0, 0, 0, NULL);
2416                 if (d < 4) {
2417                         pos = text->curc+1;
2418                 } else {
2419                         pos = text->curc-1;
2420                 }
2421         }
2422         
2423         if (d < 4) /*reading forward*/
2424         {
2425                 open = 1; 
2426                 while ( tmp ) {
2427                         while (pos <= tmp->len) {
2428                                 test[0] = (unsigned char) tmp->line[pos];
2429                                 test[1] = '\0';
2430                                 if(check_bracket(test) == d) {
2431                                         open++;
2432                                 } else if (check_bracket(test) == d+3) {
2433                                         open--;
2434                                         if (open == 0) {
2435                                                 BIF_ThemeColorBlend(TH_BACK, TH_SHADE2, 0.5);
2436                                                 glRecti(x, curarea->winy-st->lheight*(h)-2, y, curarea->winy-st->lheight*(h+1)-2);
2437
2438                                                 h= txt_get_span(text->lines.first, tmp) - st->top;
2439                                                 x2= text_draw(st, tmp->line, st->left, pos, 0, 0, 0, NULL);
2440                                                 y2= text_draw(st, tmp->line, st->left, pos+1, 0, 0, 0, NULL);
2441                                                 glRecti(x2, curarea->winy-st->lheight*(h)-2, y2, curarea->winy-st->lheight*(h+1)-2);
2442                                                 BIF_ThemeColor(TH_TEXT);
2443                                                 return;
2444                                         }
2445                                 }
2446                                 pos++;
2447                         }
2448                         tmp = tmp->next;
2449                         pos = 0;
2450                 }
2451         } else { /*  reading back */
2452                 open = 1; 
2453                 while ( tmp ) {
2454                         while (pos >= 0) {
2455                                 test[0] = (unsigned char) tmp->line[pos];
2456                                 test[1] = '\0';
2457                                 if(check_bracket(test) == d) {
2458                                         open++;
2459                                 } else if (check_bracket(test) == d-3) {
2460                                         open--;
2461                                         if (open == 0) {
2462                                                 BIF_ThemeColorBlend(TH_BACK, TH_SHADE2, 0.5);
2463                                                 glRecti(x, curarea->winy-st->lheight*(h)-2, y, curarea->winy-st->lheight*(h+1)-2);
2464
2465                                                 h= txt_get_span(text->lines.first, tmp) - st->top;
2466                                                 x2= text_draw(st, tmp->line, st->left, pos, 0, 0, 0, NULL);
2467                                                 y2= text_draw(st, tmp->line, st->left, pos+1, 0, 0, 0, NULL);
2468                                                 glRecti(x2, curarea->winy-st->lheight*(h)-2, y2, curarea->winy-st->lheight*(h+1)-2);
2469                                                 BIF_ThemeColor(TH_TEXT);
2470                                                 return;
2471                                         }
2472                                 }
2473                                 pos--;
2474                         }
2475                         tmp = tmp->prev;
2476                         if (tmp) {
2477                                 pos = tmp->len;
2478                         }
2479                 }
2480         }
2481         
2482 }
2483
2484 int check_bracket(char *string)
2485 {
2486         int number, a = 0;
2487         char other[][3] = {"(", "[", "{", ")", "]", "}"};
2488         
2489         number = 6;
2490         
2491         while(a < number) {
2492                 if(strcmp(other[a], string) == 0)
2493                 {
2494                         return a+1;
2495                 }
2496                 a++;
2497         }
2498         return 0;
2499 }
2500
2501 static int check_builtinfuncs(char *string) 
2502 {
2503         int number = 30, a = 0;
2504         
2505         char builtinfuncs[][11] = {"and", "as", "assert", "break", "class", "continue", "def",
2506                                                                 "del", "elif", "else", "except", "exec", "finally",
2507                                                                 "for", "from", "global", "if", "import", "in",
2508                                                                 "is", "lambda", "not", "or", "pass", "print",
2509                                                                 "raise", "return", "try", "while", "yield"};
2510
2511         for( a = 0; a < number; a++) {
2512                 if(!strcmp(builtinfuncs[a], string))
2513                         return 1;
2514         }
2515         return 0;
2516 }
2517
2518 static int check_specialvars(char *string) 
2519 {
2520         int number = 2, a = 0;
2521         char specialvars[][7] = {"def", "class"};
2522         
2523         for( a = 0; a < number; a++) {
2524                 if(!strcmp(specialvars[a], string))
2525                         return a+1;
2526         }
2527         return 0;
2528 }
2529
2530 static int check_delim(char *string) 
2531 {
2532         int number = 28, a = 0;
2533         char other[][3] = {"(", ")", ":", "\"", "\'", " ", "~", "!", "%", "^", "&", "*", "-", "+", "=", "[", "]", "{", "}", ";", "/", "<", ">", "|", ".", "#", "\t", ","};
2534         
2535         for( a = 0; a < number; a++) {
2536                 if(!strcmp(other[a], string))
2537                         return 1;
2538         }
2539         return 0;
2540 }
2541
2542 static int check_numbers(char *string)
2543 {
2544         int number = 10, a = 0;
2545         char other[][2] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
2546         
2547         for( a = 0; a < number; a++) {
2548                 if(!strcmp(other[a], string))
2549                         return 1;
2550         }
2551         return 0;
2552 }
2553
2554 static int check_identifier(char ch) {
2555         if (ch < '0') return 0;
2556         if (ch <= '9') return 1;
2557         if (ch < 'A') return 0;
2558         if (ch <= 'Z' || ch == '_') return 1;
2559         if (ch < 'a') return 0;
2560         if (ch <= 'z') return 1;
2561         return 0;
2562 }
2563
2564 void convert_tabs (struct SpaceText *st, int tab)
2565 {
2566         Text *text = st->text;
2567         TextLine *tmp;
2568         char *check_line, *new_line, *format;
2569         int extra, number; //unknown for now
2570         size_t a, j;
2571         
2572         if (!text) return;
2573         
2574         tmp = text->lines.first;
2575         
2576         //first convert to all space, this make it alot easier to convert to tabs because there is no mixtures of ' ' && '\t'
2577         while(tmp) {
2578                 check_line = tmp->line;
2579                 new_line = MEM_mallocN(render_string(st, check_line)+1, "Converted_Line");
2580                 format = MEM_mallocN(render_string(st, check_line)+1, "Converted_Syntax_format");
2581                 j = 0;
2582                 for (a=0; a < strlen(check_line); a++) { //foreach char in line
2583                         if(check_line[a] == '\t') { //checking for tabs
2584                                 //get the number of spaces this tabs is showing
2585                                 //i dont like doing it this way but will look into it later
2586                                 new_line[j] = '\0';
2587                                 number = render_string(st, new_line);
2588                                 new_line[j] = '\t';
2589                                 new_line[j+1] = '\0';
2590                                 number = render_string(st, new_line)-number;
2591                                 for(extra = 0; extra < number; extra++) {
2592                                         new_line[j] = ' ';
2593                                         j++;
2594                                 }
2595                         } else {
2596                                 new_line[j] = check_line[a];
2597                                 ++j;
2598                         }
2599                 }
2600                 new_line[j] = '\0';
2601                 // put new_line in the tmp->line spot still need to try and set the curc correctly
2602                 if (tmp->line) MEM_freeN(tmp->line);
2603                 if(tmp->format) MEM_freeN(tmp->format);
2604                 
2605                 tmp->line = new_line;
2606                 tmp->len = strlen(new_line);
2607                 tmp->format = format;
2608                 tmp = tmp->next;
2609         }
2610         
2611         if (tab) // Converting to tabs
2612         {       //start over from the begining
2613                 tmp = text->lines.first;
2614                 
2615                 while(tmp) {
2616                         check_line = tmp->line;
2617                         extra = 0;
2618                         for (a = 0; a < strlen(check_line); a++) {
2619                                 number = 0;
2620                                 for (j = 0; j < (size_t)st->tabnumber; j++) {
2621                                         if ((a+j) <= strlen(check_line)) { //check to make sure we are not pass the end of the line
2622                                                 if(check_line[a+j] != ' ') {
2623                                                         number = 1;
2624                                                 }
2625                                         }
2626                                 }
2627                                 if (!number) { //found all number of space to equal a tab
2628                                         a = a+(st->tabnumber-1);
2629                                         extra = extra+1;
2630                                 }
2631                         }
2632                         
2633                         if ( extra > 0 ) { //got tabs make malloc and do what you have to do
2634                                 new_line = MEM_mallocN(strlen(check_line)-(((st->tabnumber*extra)-extra)-1), "Converted_Line");
2635                                 format = MEM_mallocN(strlen(check_line)-(((st->tabnumber*extra)-extra)-1), "Converted_Syntax_format");
2636                                 extra = 0; //reuse vars
2637                                 for (a = 0; a < strlen(check_line); a++) {
2638                                         number = 0;
2639                                         for (j = 0; j < (size_t)st->tabnumber; j++) {
2640                                                 if ((a+j) <= strlen(check_line)) { //check to make sure we are not pass the end of the line
2641                                                         if(check_line[a+j] != ' ') {
2642                                                                 number = 1;
2643                                                         }
2644                                                 }
2645                                         }
2646                                         if (!number) { //found all number of space to equal a tab
2647                                                 new_line[extra] = '\t';
2648                                                 a = a+(st->tabnumber-1);
2649                                                 ++extra;
2650                                                 
2651                                         } else { //not adding a tab
2652                                                 new_line[extra] = check_line[a];
2653                                                 ++extra;
2654                                         }
2655                                 }
2656                                 new_line[extra] = '\0';
2657                                 // put new_line in the tmp->line spot still need to try and set the curc correctly
2658                                 if (tmp->line) MEM_freeN(tmp->line);
2659                                 if(tmp->format) MEM_freeN(tmp->format);
2660                                 
2661                                 tmp->line = new_line;
2662                                 tmp->len = strlen(new_line);
2663                                 tmp->format = format;
2664                         }
2665                         tmp = tmp->next;
2666                 }
2667         }
2668
2669         if (st->showsyntax)
2670                 get_format_string(st);
2671 }