f560addf455f9bef05b4c8b00a3187013badaecb
[blender.git] / source / blender / src / drawtext.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL 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. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32
33 #include <stdlib.h>
34 #include <math.h>
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39
40 #ifndef _WIN32
41 #include <unistd.h>
42 #else
43 #include <io.h>
44 #include "BLI_winstuff.h"
45 #endif   
46 #include "MEM_guardedalloc.h"
47 #include "PIL_time.h"
48
49 #include "BMF_Api.h"
50
51 #include "BLI_blenlib.h"
52 #include "BLI_arithb.h"
53 #include "BLI_editVert.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
65 #include "BPY_extern.h"
66
67 #include "BIF_gl.h"
68 #include "BIF_keyval.h"
69 #include "BIF_interface.h"
70 #include "BIF_drawtext.h"
71 #include "BIF_spacetypes.h"
72 #include "BIF_usiblender.h"
73 #include "BIF_screen.h"
74 #include "BIF_toolbox.h"
75 #include "BIF_space.h"
76 #include "BIF_mywindow.h"
77
78 #include "BSE_filesel.h"
79
80 #include "mydevice.h"
81 #include "blendef.h" 
82 #include "interface.h"
83
84 #define TEXTXLOC        38
85
86 /* locals */
87 void drawtextspace(void);
88 void winqreadtextspace(unsigned short event, short val, char ascii);
89
90 static void *last_txt_find_string= NULL;
91
92 static BMF_Font *spacetext_get_font(SpaceText *st) {
93         static BMF_Font *scr12= NULL;
94         static BMF_Font *scr15= NULL;
95         
96         switch (st->font_id) {
97         default:
98         case 0:
99                 if (!scr12)
100                         scr12= BMF_GetFont(BMF_kScreen12);
101                 return scr12;
102         case 1:
103                 if (!scr15)
104                         scr15= BMF_GetFont(BMF_kScreen15);
105                 return scr15;
106         }
107 }
108
109 static int spacetext_get_fontwidth(SpaceText *st) {
110         return BMF_GetCharacterWidth(spacetext_get_font(st), ' ');
111 }
112
113 static char *temp_char_buf= NULL;
114 static int *temp_char_accum= NULL;
115 static int temp_char_len= 0;
116 static int temp_char_pos= 0;
117
118 static void temp_char_write(char c, int accum) {
119         if (temp_char_len==0 || temp_char_pos>=temp_char_len) {
120                 char *nbuf; int *naccum;
121                 int olen= temp_char_len;
122                 
123                 if (olen) temp_char_len*= 2;
124                 else temp_char_len= 256;
125                 
126                 nbuf= MEM_mallocN(sizeof(*temp_char_buf)*temp_char_len, "temp_char_buf");
127                 naccum= MEM_mallocN(sizeof(*temp_char_accum)*temp_char_len, "temp_char_accum");
128                 
129                 if (olen) {
130                         memcpy(nbuf, temp_char_buf, olen);
131                         memcpy(naccum, temp_char_accum, olen);
132                         
133                         MEM_freeN(temp_char_buf);
134                         MEM_freeN(temp_char_accum);
135                 }
136                 
137                 temp_char_buf= nbuf;
138                 temp_char_accum= naccum;
139         }
140         
141         temp_char_buf[temp_char_pos]= c;        
142         temp_char_accum[temp_char_pos]= accum;
143         
144         if (c==0) temp_char_pos= 0;
145         else temp_char_pos++;
146 }
147
148 void free_txt_data(void) {
149         txt_free_cut_buffer();
150         
151         if (last_txt_find_string) MEM_freeN(last_txt_find_string);
152         if (temp_char_buf) MEM_freeN(temp_char_buf);
153         if (temp_char_accum) MEM_freeN(temp_char_accum);        
154 }
155
156 static int render_string (char *in) {
157         int r= 0, i;
158         
159         while(*in) {
160                 if (*in=='\t') {
161                         if (temp_char_pos && *(in-1)=='\t') i= TXT_TABSIZE;
162                         else i= TXT_TABSIZE - (temp_char_pos%TXT_TABSIZE);
163
164                         while(i--) temp_char_write(' ', r);
165                 } else temp_char_write(*in, r);
166
167                 r++;
168                 in++;
169         }
170         r= temp_char_pos;
171         temp_char_write(0, 0);
172                 
173         return r;
174 }
175
176 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y) {
177         int r=0, w= 0;
178         char *in;
179         int *acc;
180
181         w= render_string(str);
182         if(w<cshift ) return 0; /* String is shorter than shift */
183         
184         in= temp_char_buf+cshift;
185         acc= temp_char_accum+cshift;
186         w= w-cshift;
187         
188         if (draw) {
189                 glRasterPos2i(x, y);
190                 BMF_DrawString(spacetext_get_font(st), in);
191         } else {
192                 while (w-- && *acc++ < maxwidth) {
193                         r+= spacetext_get_fontwidth(st);
194                 }
195         }
196
197         if (cshift && r==0) return 0;
198         else if (st->showlinenrs)
199                 return r+TXT_OFFSET+TEXTXLOC;
200         else
201                 return r+TXT_OFFSET;
202 }
203
204 static void set_cursor_to_pos (SpaceText *st, int x, int y, int sel) 
205 {
206         Text *text;
207         TextLine **linep;
208         int *charp;
209         int w;
210         
211         text= st->text;
212
213         if(sel) { linep= &text->sell; charp= &text->selc; } 
214         else { linep= &text->curl; charp= &text->curc; }
215         
216         y= (curarea->winy - y)/st->lheight;
217         
218         y-= txt_get_span(text->lines.first, *linep) - st->top;
219         
220         if (y>0) {
221                 while (y-- != 0) if((*linep)->next) *linep= (*linep)->next;
222         } else if (y<0) {
223                 while (y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
224         }
225
226         if(st->showlinenrs)
227                 x-= TXT_OFFSET+TEXTXLOC;
228         else
229                 x-= TXT_OFFSET;
230
231         if (x<0) x= 0;
232         x = (x/spacetext_get_fontwidth(st)) + st->left;
233         
234         w= render_string((*linep)->line);
235         if(x<w) *charp= temp_char_accum[x];
236         else *charp= (*linep)->len;
237         
238         if(!sel) txt_pop_sel(text);
239 }
240
241 static void draw_cursor(SpaceText *st) {
242         int h, x, i;
243         Text *text= st->text;
244         TextLine *linef, *linel;
245         int charf, charl;
246         
247         if (text->curl==text->sell && text->curc==text->selc) {
248                 x= text_draw(st, text->curl->line, st->left, text->curc, 0, 0, 0);
249
250                 if (x) {
251                         h= txt_get_span(text->lines.first, text->curl) - st->top;
252
253                         glColor3f(1.0, 0.0, 0.0);
254                         
255                         glRecti(x-1, curarea->winy-st->lheight*(h)-2, x+1, curarea->winy-st->lheight*(h+1)-2);
256                 }
257         } else {
258                 int span= txt_get_span(text->curl, text->sell);
259                 
260                 if (span<0) {
261                         linef= text->sell;
262                         charf= text->selc;
263                         
264                         linel= text->curl;
265                         charl= text->curc;
266                 } else if (span>0) {
267                         linef= text->curl;
268                         charf= text->curc;
269         
270                         linel= text->sell;              
271                         charl= text->selc;
272                 } else {
273                         linef= linel= text->curl;
274                         
275                         if (text->curc<text->selc) {
276                                 charf= text->curc;
277                                 charl= text->selc;
278                         } else {
279                                 charf= text->selc;
280                                 charl= text->curc;
281                         }
282                 }
283         
284                         /* Walk to the beginning of visible text */
285                 h= txt_get_span(text->lines.first, linef) - st->top;
286                 while (h++<-1 && linef!=linel) linef= linef->next;
287         
288                 x= text_draw(st, linef->line, st->left, charf, 0, 0, 0);
289
290                 glColor3f(0.75, 0.44, 0.44);
291
292                 if(st->showlinenrs)
293                         if (!x) x= TXT_OFFSET + TEXTXLOC -4;
294                 else
295                         if (!x) x= TXT_OFFSET - 4;
296
297                 if (!x) x= TXT_OFFSET-10;
298                 while (linef && linef != linel) {
299                         h= txt_get_span(text->lines.first, linef) - st->top;
300                         if (h>st->viewlines) break;
301                         
302                         glRecti(x, curarea->winy-st->lheight*(h)-2, curarea->winx, curarea->winy-st->lheight*(h+1)-2);
303                         if(st->showlinenrs)
304                                 glRecti(TXT_OFFSET+TEXTXLOC-4, curarea->winy-st->lheight*(h+1)-2, TXT_OFFSET+TEXTXLOC, curarea->winy-st->lheight*(h+2)-2);
305                         else
306                                 glRecti(TXT_OFFSET-4, curarea->winy-st->lheight*(h+1)-2, TXT_OFFSET, curarea->winy-st->lheight*(h+2)-2);
307
308                         if(st->showlinenrs)
309                                 x= TXT_OFFSET + TEXTXLOC;
310                         else
311                                 x= TXT_OFFSET;
312                         
313                         linef= linef->next;
314                 }
315                 
316                 h= txt_get_span(text->lines.first, linef) - st->top;
317
318                 i= text_draw(st, linel->line, st->left, charl, 0, 0, 0);
319                 if(i) glRecti(x, curarea->winy-st->lheight*(h)-2, i, curarea->winy-st->lheight*(h+1)-2);
320 // draw cursor in selection
321 //              h= txt_get_span(text->lines.first, text->curl) - st->top;
322 //              glColor3f(1.0, 0.0, 0.0);
323 //              glRecti(x2-1, curarea->winy-st->lheight*(h)-2, x2+1, curarea->winy-st->lheight*(h+1)-2);
324         }
325
326         glColor3f(0.0, 0.0, 0.0);
327 }
328
329 static void calc_text_rcts(SpaceText *st)
330 {
331         short barheight, barstart;
332         int lbarstart, lbarh, ltexth;
333
334         lbarstart= st->top;
335         lbarh=  st->viewlines;
336         ltexth= txt_get_span(st->text->lines.first, st->text->lines.last)+1;
337
338         barheight= (lbarh*(curarea->winy-4))/ltexth;
339         if (barheight<20) barheight=20;
340         
341         barstart= (lbarstart*(curarea->winy-4))/ltexth + 8;
342
343         st->txtbar.xmin= 5;
344         st->txtbar.xmax= 17;
345         st->txtbar.ymax= curarea->winy - barstart;
346         st->txtbar.ymin= st->txtbar.ymax - barheight;
347
348         CLAMP(st->txtbar.ymin, 2, curarea->winy-2);
349         CLAMP(st->txtbar.ymax, 2, curarea->winy-2);
350
351         st->pix_per_line= (float) ltexth/curarea->winy;
352         if (st->pix_per_line<.1) st->pix_per_line=.1;
353
354         lbarstart= MIN2(txt_get_span(st->text->lines.first, st->text->curl), 
355                                 txt_get_span(st->text->lines.first, st->text->sell));
356         lbarh= abs(txt_get_span(st->text->lines.first, st->text->curl)-txt_get_span(st->text->lines.first, st->text->sell));
357         
358         barheight= (lbarh*(curarea->winy-4))/ltexth;
359         if (barheight<2) barheight=2; 
360         
361         barstart= (lbarstart*(curarea->winy-4))/ltexth + 8;
362         
363         st->txtscroll.xmin= 5;
364         st->txtscroll.xmax= 17;
365         st->txtscroll.ymax= curarea->winy-barstart;
366         st->txtscroll.ymin= st->txtscroll.ymax - barheight;
367
368         CLAMP(st->txtscroll.ymin, 2, curarea->winy-2);
369         CLAMP(st->txtscroll.ymax, 2, curarea->winy-2);
370 }
371
372 static void draw_textscroll(SpaceText *st)
373 {
374         if (!st->text) return;
375
376         calc_text_rcts(st);
377         
378         cpack(0x707070);
379         glRecti(2, 2, 20, curarea->winy-6);
380         uiEmboss(2, 2, 20, curarea->winy-6, 1);
381
382         cpack(0x909090);
383         glRecti(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax);
384
385         cpack(0x7777c6);
386         glRecti(st->txtscroll.xmin, st->txtscroll.ymin, st->txtscroll.xmax, st->txtscroll.ymax);
387
388         uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
389 }
390
391 static void screen_skip(SpaceText *st, int lines)
392 {
393         int last;
394         
395         if (!st) return;
396         if (st->spacetype != SPACE_TEXT) return;
397         if (!st->text) return;
398
399         st->top += lines;
400
401         last= txt_get_span(st->text->lines.first, st->text->lines.last);
402         last= last - (st->viewlines/2);
403         
404         if (st->top>last) st->top= last;
405         if (st->top<0) st->top= 0;
406 }
407
408 /* 
409  * mode 1 == view scroll
410  * mode 2 == scrollbar
411  */
412 static void do_textscroll(SpaceText *st, int mode)
413 {
414         short delta[2]= {0, 0};
415         short mval[2], hold[2], old[2];
416         
417         if (!st->text) return;
418         
419         calc_text_rcts(st);
420
421         st->flags|= ST_SCROLL_SELECT;
422
423         glDrawBuffer(GL_FRONT);
424         uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
425         glDrawBuffer(GL_BACK);
426
427         getmouseco_areawin(mval);
428         old[0]= hold[0]= mval[0];
429         old[1]= hold[1]= mval[1];
430
431         while(get_mbut()&(L_MOUSE|M_MOUSE)) {
432                 getmouseco_areawin(mval);
433
434                 if(old[0]!=mval[0] || old[1]!=mval[1]) {
435                         if (mode==1) {
436                                 delta[0]= (hold[0]-mval[0])/spacetext_get_fontwidth(st);
437                                 delta[1]= (mval[1]-hold[1])/st->lheight;
438                         }
439                         else delta[1]= (hold[1]-mval[1])*st->pix_per_line;
440                         
441                         if (delta[0] || delta[1]) {
442                                 screen_skip(st, delta[1]);
443                                 st->left+= delta[0];
444                                 if (st->left<0) st->left= 0;
445                                 
446                                 scrarea_do_windraw(curarea);
447                                 screen_swapbuffers();
448                                 
449                                 hold[0]=mval[0];
450                                 hold[1]=mval[1];
451                         }
452                         old[0]=mval[0];
453                         old[1]=mval[1];
454                 } else {
455                         BIF_wait_for_statechange();
456                 }
457         }
458         st->flags^= ST_SCROLL_SELECT;
459
460         glDrawBuffer(GL_FRONT);
461         uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
462         glDrawBuffer(GL_BACK);
463 }
464
465 static void do_selection(SpaceText *st, int selecting)
466 {
467         short mval[2], old[2];
468         int sell, selc;
469         int linep2, charp2;
470         int first= 1;
471
472         getmouseco_areawin(mval);
473         old[0]= mval[0];
474         old[1]= mval[1];
475
476         if (!selecting) {
477                 int curl= txt_get_span(st->text->lines.first, st->text->curl);
478                 int curc= st->text->curc;                       
479                 int linep2, charp2;
480                                         
481                 set_cursor_to_pos(st, mval[0], mval[1], 0);
482
483                 linep2= txt_get_span(st->text->lines.first, st->text->curl);
484                 charp2= st->text->selc;
485                                 
486                 if (curl!=linep2 || curc!=charp2)
487                         txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
488         }
489
490         sell= txt_get_span(st->text->lines.first, st->text->sell);
491         selc= st->text->selc;
492
493         while(get_mbut()&L_MOUSE) {
494                 getmouseco_areawin(mval);
495
496                 if (mval[1]<0 || mval[1]>curarea->winy) {
497                         int d= (old[1]-mval[1])*st->pix_per_line;
498                         if (d) screen_skip(st, d);
499
500                         set_cursor_to_pos(st, mval[0], mval[1]<0?0:curarea->winy, 1);
501
502                         scrarea_do_windraw(curarea);
503                         screen_swapbuffers();
504                 } else if (mval[0]<0 || mval[0]>curarea->winx) {
505                         if (mval[0]>curarea->winx) st->left++;
506                         else if (mval[0]<0 && st->left>0) st->left--;
507                         
508                         set_cursor_to_pos(st, mval[0], mval[1], 1);
509                         
510                         scrarea_do_windraw(curarea);
511                         screen_swapbuffers();
512                         
513                         PIL_sleep_ms(10);
514                 } else if (first || old[0]!=mval[0] || old[1]!=mval[1]) {
515                         set_cursor_to_pos(st, mval[0], mval[1], 1);
516
517                         scrarea_do_windraw(curarea);
518                         screen_swapbuffers();
519
520                         old[0]= mval[0];
521                         old[1]= mval[1];
522                         first= 1;
523                 } else {
524                         BIF_wait_for_statechange();
525                 }
526         }
527
528         linep2= txt_get_span(st->text->lines.first, st->text->sell);
529         charp2= st->text->selc;
530                 
531         if (sell!=linep2 || selc!=charp2)
532                 txt_undo_add_toop(st->text, UNDO_STO, sell, selc, linep2, charp2);
533 }
534
535 void drawtextspace(void)
536 {
537         SpaceText *st= curarea->spacedata.first;
538         Text *text;
539         int i;
540         TextLine *tmp;
541         char linenr[12];
542         int linecount = 0;
543
544         if (BPY_spacetext_is_pywin(st)) {
545                 BPY_spacetext_do_pywin_draw(st);
546                 return;
547         }
548         
549         glClearColor(0.6, 0.6,  0.6, 1.0);
550         glClear(GL_COLOR_BUFFER_BIT);
551         myortho2(-0.5, curarea->winrct.xmax-curarea->winrct.xmin-0.5, -0.5, curarea->winrct.ymax-curarea->winrct.ymin-0.5);
552
553         text= st->text;
554         if(!text) return;
555         
556         /* Make sure all the positional pointers exist */
557         if (!text->curl || !text->sell || !text->lines.first || !text->lines.last)
558                 txt_clean_text(text);
559         
560         if(st->lheight) st->viewlines= (int) curarea->winy/st->lheight;
561         else st->viewlines= 0;
562         
563         if(st->showlinenrs) {
564                 cpack(C_DERK);
565 //weird bug where glRect draws one pixel off when window is fullscreen
566 //nvidia related !!!
567 //              if(curarea->full)
568 //                      glRecti(23,  0, (st->lheight==15)?63:59,  curarea->winy - 2);
569 //              else
570                         glRecti(24,  0, (st->lheight==15)?64:60,  curarea->winy - 2);
571         }
572
573         glColor3f(0.0, 0.0, 0.0);
574
575         draw_cursor(st);
576
577         tmp= text->lines.first;
578         for (i= 0; i<st->top && tmp; i++) {
579                 tmp= tmp->next;
580                 linecount++;
581         }
582         for (i=0; i<st->viewlines && tmp; i++, tmp= tmp->next) {
583                 if(st->showlinenrs) {
584                         if(((float)(i + linecount + 1)/10000.0) < 1.0) {
585                                 sprintf(linenr, "%04d", i + linecount + 1);
586                                 glRasterPos2i(TXT_OFFSET - 7, curarea->winy-st->lheight*(i+1));
587                         } else {
588                                 sprintf(linenr, "%05d", i + linecount + 1);
589                                 glRasterPos2i(TXT_OFFSET - 11, curarea->winy-st->lheight*(i+1));
590                         }
591                         BMF_DrawString(spacetext_get_font(st), linenr);
592                         text_draw(st, tmp->line, st->left, 0, 1, TXT_OFFSET + TEXTXLOC, curarea->winy-st->lheight*(i+1));
593                 } else
594                         text_draw(st, tmp->line, st->left, 0, 1, TXT_OFFSET, curarea->winy-st->lheight*(i+1));
595         }
596
597         draw_textscroll(st);
598
599         curarea->win_swap= WIN_BACK_OK;
600 }
601
602 void pop_space_text (SpaceText *st)
603 {
604         int i, x;
605
606         if(!st) return;
607         if(!st->text) return;
608         if(!st->text->curl) return;
609                 
610         i= txt_get_span(st->text->lines.first, st->text->curl);
611         if (st->top+st->viewlines <= i || st->top > i) {
612                 st->top= i - st->viewlines/2;
613         }
614         
615         x= text_draw(st, st->text->curl->line, st->left, st->text->curc, 0, 0, 0);
616
617         if (x==0 || x>curarea->winx) {
618                 st->left= st->text->curc-0.5*(curarea->winx)/spacetext_get_fontwidth(st);
619         }
620
621         if (st->top < 0) st->top= 0;
622         if (st->left <0) st->left= 0;
623 }
624
625 void add_text_fs(char *file) 
626 {
627         SpaceText *st= curarea->spacedata.first;
628         Text *text;
629
630         if (!st) return;
631         if (st->spacetype != SPACE_TEXT) return;
632
633         text= add_text(file);
634
635         st->text= text;
636
637         st->top= 0;
638                         
639         allqueue(REDRAWTEXT, 0);
640         allqueue(REDRAWHEADERS, 0);     
641 }
642
643 void free_textspace(SpaceText *st)
644 {
645         if (!st) return;
646
647         st->text= NULL;
648 }
649
650 static void save_mem_text(char *str)
651 {
652         SpaceText *st= curarea->spacedata.first;
653         Text *text;
654         
655         if (!str) return;
656         
657         if (!st) return;
658         if (st->spacetype != SPACE_TEXT) return;
659
660         text= st->text;
661         if(!text) return;
662         
663         if (text->name) MEM_freeN(text->name);
664         text->name= MEM_mallocN(strlen(str)+1, "textname");
665         strcpy(text->name, str);
666
667         text->flags ^= TXT_ISMEM;
668                 
669         txt_write_file(text);
670 }
671
672 void txt_write_file(Text *text) 
673 {
674         FILE *fp;
675         TextLine *tmp;
676         
677         /* Do we need to get a filename? */
678         if (text->flags & TXT_ISMEM) {
679                 activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", G.sce, save_mem_text);
680                 return; 
681         }
682         
683         /* Should we ask to save over? */
684         if (text->flags & TXT_ISTMP) {
685                 if (BLI_exists(text->name)) {
686                         if (!okee("Save over?")) return;
687                 } else if (!okee("Create new file?")) return;
688
689                 text->flags ^= TXT_ISTMP;
690         }
691                 
692         fp= fopen(text->name, "w");
693         if (fp==NULL) {
694                 error("Unable to save file");
695                 return;
696         }
697
698         tmp= text->lines.first;
699         while (tmp) {
700                 if (tmp->next) fprintf(fp, "%s\n", tmp->line);
701                 else fprintf(fp, "%s", tmp->line);
702                 
703                 tmp= tmp->next;
704         }
705         
706         fclose (fp);
707         
708         if (text->flags & TXT_ISDIRTY) text->flags ^= TXT_ISDIRTY;
709 }
710
711 void unlink_text(Text *text)
712 {
713         bScreen *scr;
714         ScrArea *area;
715         SpaceLink *sl;
716         
717         for (scr= G.main->screen.first; scr; scr= scr->id.next) {
718                 for (area= scr->areabase.first; area; area= area->next) {
719                         for (sl= area->spacedata.first; sl; sl= sl->next) {
720                                 if (sl->spacetype==SPACE_TEXT) {
721                                         SpaceText *st= (SpaceText*) sl;
722                                         
723                                         if (st->text==text) {
724                                                 st->text= NULL;
725                                                 st->top= 0;
726                                                 
727                                                 if (st==area->spacedata.first) {
728                                                         scrarea_queue_redraw(area);
729                                                 }
730                                         }
731                                 }
732                         }
733                 }
734         }
735 }
736
737 static int jumptoline_interactive(SpaceText *st) {
738         short nlines= txt_get_span(st->text->lines.first, st->text->lines.last)+1;
739         short tmp= txt_get_span(st->text->lines.first, st->text->curl)+1;
740
741         if (button(&tmp, 1, nlines, "Jump to line:")) {
742                 txt_move_toline(st->text, tmp-1, 0);
743                 pop_space_text(st);
744                 return 1;
745         } else {
746                 return 0;
747         }
748 }
749
750 void winqreadtextspace(unsigned short event, short val, char ascii)
751 {
752         SpaceText *st= curarea->spacedata.first;
753         Text *text= st->text;
754         char *py_filename;
755         int do_draw=0, p;
756
757         if (BPY_spacetext_is_pywin(st)) {
758                 BPY_spacetext_do_pywin_event(st, event, val);
759                 return;
760         }
761
762         text= st->text;
763         
764         if (!text) {
765                 if (val && !ELEM(G.qual, 0, LR_SHIFTKEY)) {
766                         if (event==FKEY && (G.qual & LR_ALTKEY) && (G.qual & LR_SHIFTKEY)) {
767                                 switch (pupmenu("File %t|New %x0|Open... %x1")) {
768                                 case 0:
769                                         st->text= add_empty_text();
770                                         st->top= 0;
771                                 
772                                         allqueue(REDRAWTEXT, 0);
773                                         allqueue(REDRAWHEADERS, 0);
774                                         break;
775                                 case 1:
776                                         activate_fileselect(FILE_SPECIAL, "LOAD TEXT FILE", G.sce, add_text_fs);
777                                         break;
778                                 }
779                         } else if (event==QKEY) {
780                                 if(okee("QUIT BLENDER")) exit_usiblender();
781                         }
782                 }
783                 
784                 return;
785         }
786         
787         if (event==LEFTMOUSE) {
788                 if (val) {
789                         short mval[2];
790
791                         getmouseco_areawin(mval);
792
793                         if (mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<curarea->winy-2) {
794                                 do_textscroll(st, 2);
795                         } else {                        
796                                 do_selection(st, G.qual&LR_SHIFTKEY);
797                                 do_draw= 1;
798                         }
799                 }
800         } else if (event==MIDDLEMOUSE) {
801                 if (val) {
802                         do_textscroll(st, 1);
803                 }
804         } else if (ascii) {
805                 if (txt_add_char(text, ascii)) {
806                         pop_space_text(st);
807                         do_draw= 1;
808                 }
809         } else if (val) {
810                 switch (event) {
811                 case FKEY:
812                         if ((G.qual & LR_ALTKEY) && (G.qual & LR_SHIFTKEY)) {
813                                 p= pupmenu("File %t|New %x0|Open... %x1|Save %x2|Save As...%x3");
814
815                                 switch(p) {
816                                 case 0:
817                                         st->text= add_empty_text();
818                                         st->top= 0;
819                                         
820                                         allqueue(REDRAWTEXT, 0);
821                                         allqueue(REDRAWHEADERS, 0);
822                                         break;
823
824                                 case 1:
825                                         activate_fileselect(FILE_SPECIAL, "LOAD TEXT FILE", G.sce, add_text_fs);
826                                         break;
827
828                                 case 3:
829                                         text->flags |= TXT_ISMEM;
830                                         
831                                 case 2:
832                                         txt_write_file(text);
833                                         do_draw= 1;
834                                         break;
835
836                                 default:
837                                         break;
838                                 }
839                         } else if (G.qual & LR_ALTKEY) {
840                                 char *findstr= last_txt_find_string;
841                                 
842                                 if (txt_has_sel(text) && !(G.qual & LR_CTRLKEY)) {
843                                         findstr= txt_sel_to_buf(text);
844                                 } else if (!last_txt_find_string || (G.qual & LR_CTRLKEY)) {
845                                         char buf[256];
846
847                                         if (findstr && strlen(findstr)<(sizeof(buf)-1))
848                                                 strcpy(buf, findstr);
849                                         else
850                                                 buf[0]= 0;
851                                         
852                                         if (sbutton(buf, 0, sizeof(buf)-1, "Find: ") && buf[0])
853                                                 findstr= BLI_strdup(buf);
854                                         else
855                                                 findstr= NULL;
856                                 }
857
858                                 if (findstr!=last_txt_find_string) {
859                                         if (last_txt_find_string)
860                                                 MEM_freeN(last_txt_find_string);
861                                         last_txt_find_string= findstr;
862                                 }
863                                 
864                                 if (findstr) {
865                                         if (txt_find_string(text, findstr))
866                                                 pop_space_text(st);
867                                         else
868                                                 error("Not found: %s", findstr);
869                                 }
870                                         
871                                 do_draw= 1;
872                         }
873                         
874                         break;
875
876                 case EKEY:
877                         if (G.qual & LR_ALTKEY && G.qual & LR_SHIFTKEY) {
878                                 p= pupmenu("Edit %t|"
879                                                         "Cut %x0|"
880                                                         "Copy %x1|"
881                                                         "Paste %x2|"
882                                                         "Print Cut Buffer %x3");
883                                 switch(p) {
884                                 case 0:
885                                         txt_cut_sel(text);
886                                         do_draw= 1;
887                                         break;
888                                 case 1:
889                                         txt_copy_sel(text);
890                                         do_draw= 1;
891                                         break;
892                                 case 2:
893                                         txt_paste(text);
894                                         do_draw= 1;
895                                         break;
896                                 case 3:
897                                         txt_print_cutbuffer();
898                                         break;
899                                 }
900                         }
901                         break;
902
903                 case VKEY:
904                         if (G.qual & LR_ALTKEY && G.qual & LR_SHIFTKEY) {
905                                 p= pupmenu("View %t|"
906                                                         "Top of File %x0|"
907                                                         "Bottom of File %x1|"
908                                                         "Page Up %x2|"
909                                                         "Page Down %x3");
910                                 switch(p) {
911                                 case 0:
912                                         txt_move_bof(text, 0);
913                                         do_draw= 1;
914                                         pop_space_text(st);
915                                         break;
916                                         
917                                 case 1:
918                                         txt_move_eof(text, 0);
919                                         do_draw= 1;
920                                         pop_space_text(st);
921                                         break;
922                                         
923                                 case 2:
924                                         screen_skip(st, -st->viewlines);
925                                         do_draw= 1;
926                                         break;
927                                         
928                                 case 3:
929                                         screen_skip(st, st->viewlines);
930                                         do_draw= 1;
931                                         break;
932                                 }
933                         }
934                         break;
935
936                 case SKEY:
937                         if (G.qual & LR_ALTKEY && G.qual & LR_SHIFTKEY) {
938                                 p= pupmenu("Select %t|"
939                                                         "Select All %x0|"
940                                                         "Select Line %x1|"
941                                                         "Jump to Line %x3");
942                                 switch(p) {
943                                 case 0:
944                                         txt_sel_all(text);
945                                         do_draw= 1;
946                                         break;
947                                         
948                                 case 1:
949                                         txt_sel_line(text);
950                                         do_draw= 1;
951                                         break;
952                                                                                 
953                                 case 3:
954                                         do_draw= jumptoline_interactive(st);
955                                         break;
956                                 }
957                         }
958                         break;
959                         
960                 case QKEY:
961                         if(okee("QUIT BLENDER")) exit_usiblender();
962                         break;
963                 }
964
965                 switch(event) {
966                 case AKEY:
967                         if (G.qual & LR_CTRLKEY) {
968                                 txt_move_bol(text, G.qual & LR_SHIFTKEY);
969                                 do_draw= 1;
970                                 pop_space_text(st);
971                         } else if (G.qual & LR_ALTKEY) {
972                                 txt_sel_all(text);
973                                 do_draw= 1;
974                         }
975                         break;
976
977                 case CKEY:
978                         if (G.qual & LR_ALTKEY) {
979                                 txt_copy_sel(text);
980                                 do_draw= 1;     
981                         }
982                         break;
983
984                 case DKEY:
985                         if (G.qual & LR_CTRLKEY) {
986                                 txt_delete_char(text);
987                                 do_draw= 1;
988                                 pop_space_text(st);
989                         }
990                         break;
991
992                 case EKEY:
993                         if (G.qual & LR_CTRLKEY) {
994                                 txt_move_eol(text, G.qual & LR_SHIFTKEY);
995                                 do_draw= 1;
996                                 pop_space_text(st);
997                         }
998                         break;
999
1000                 case JKEY:
1001                         if (G.qual & LR_ALTKEY) {
1002                                 do_draw= jumptoline_interactive(st);
1003                         }
1004                         break;
1005
1006                 case OKEY:
1007                         if (G.qual & LR_ALTKEY) {
1008                                 activate_fileselect(FILE_SPECIAL, "LOAD TEXT FILE", G.sce, add_text_fs);
1009                         }
1010                         break;
1011                         
1012                 case PKEY:
1013                         if (G.qual & LR_ALTKEY) {
1014                                 if (!BPY_txt_do_python(st)) {
1015                                         int lineno = BPY_Err_getLinenumber();
1016                                         // jump to error if happened in current text:
1017                                         py_filename = (char*) BPY_Err_getFilename();
1018                                         if (!strcmp(py_filename, st->text->id.name+2)) {
1019                                                 error("Python script error, check console");
1020                                                 if (lineno >= 0) {
1021                                                         txt_move_toline(text, lineno-1, 0);
1022                                                         txt_sel_line(text);
1023                                                         do_draw= 1;
1024                                                         pop_space_text(st);
1025                                                 }       
1026                                         } else {
1027                                                 error("Error in other (possibly external) file, "\
1028                                                 "check console");
1029                                         }       
1030                                 }
1031                         }
1032                         break;
1033                         
1034                 case RKEY:
1035                         if (G.qual & LR_ALTKEY) {
1036                                 txt_do_redo(text);
1037                                 do_draw= 1;     
1038                         }
1039                         if (G.qual & LR_CTRLKEY) {
1040                                 if (text->compiled) BPY_free_compiled_text(text);
1041                                 text->compiled = NULL;
1042                                 if (okee("Reopen Text")) {
1043                                         if (!reopen_text(text)) {
1044                                                 error("Could not reopen file");
1045                                         }
1046                                 }
1047                                 do_draw= 1;     
1048                         }
1049                         break;
1050                 
1051                 case SKEY:
1052                         if (G.qual & LR_ALTKEY) {
1053                                 if (G.qual & LR_SHIFTKEY) 
1054                                         if (text) text->flags |= TXT_ISMEM;
1055                                         
1056                                 txt_write_file(text);
1057                                 do_draw= 1;
1058                         }
1059                         break;
1060                         
1061                 case UKEY:
1062                         if (G.qual & LR_ALTKEY) {
1063                                 if (G.qual & LR_SHIFTKEY) txt_print_undo(text);
1064                                 else {
1065                                         txt_do_undo(text);
1066                                         do_draw= 1;
1067                                 }
1068                         }
1069                         break;
1070
1071                 case VKEY:
1072                         if (G.qual & LR_ALTKEY) {
1073                                 txt_paste(text);
1074                                 do_draw= 1;     
1075                                 pop_space_text(st);
1076                         }
1077                         break;
1078
1079                 case XKEY:
1080                         if (G.qual & LR_ALTKEY) {
1081                                 txt_cut_sel(text);
1082                                 do_draw= 1;     
1083                                 pop_space_text(st);
1084                         }
1085                         break;
1086                 
1087                 case TABKEY:
1088                         txt_add_char(text, '\t');
1089                         pop_space_text(st);
1090                         do_draw= 1;
1091                         break;
1092
1093                 case RETKEY:
1094                         txt_split_curline(text);
1095                         do_draw= 1;
1096                         pop_space_text(st);
1097                         break;
1098
1099                 case BACKSPACEKEY:
1100                         txt_backspace_char(text);
1101                         do_draw= 1;
1102                         pop_space_text(st);
1103                         break;
1104
1105                 case DELKEY:
1106                         txt_delete_char(text);
1107                         do_draw= 1;
1108                         pop_space_text(st);
1109                         break;
1110
1111                 case DOWNARROWKEY:
1112                         txt_move_down(text, G.qual & LR_SHIFTKEY);
1113                         do_draw= 1;
1114                         pop_space_text(st);
1115                         break;
1116
1117                 case LEFTARROWKEY:
1118                         txt_move_left(text, G.qual & LR_SHIFTKEY);
1119                         do_draw= 1;
1120                         pop_space_text(st);
1121                         break;
1122
1123                 case RIGHTARROWKEY:
1124                         txt_move_right(text, G.qual & LR_SHIFTKEY);
1125                         do_draw= 1;
1126                         pop_space_text(st);
1127                         break;
1128
1129                 case UPARROWKEY:
1130                         txt_move_up(text, G.qual & LR_SHIFTKEY);
1131                         do_draw= 1;
1132                         pop_space_text(st);
1133                         break;
1134
1135                 case PAGEDOWNKEY:
1136                         screen_skip(st, st->viewlines);
1137                         do_draw= 1;
1138                         break;
1139
1140                 case PAGEUPKEY:
1141                         screen_skip(st, -st->viewlines);
1142                         do_draw= 1;
1143                         break;
1144
1145                 case WHEELUPMOUSE:
1146                         screen_skip(st, -U.wheellinescroll);
1147                         do_draw= 1;
1148                         break;
1149
1150                 case WHEELDOWNMOUSE:
1151                         screen_skip(st, U.wheellinescroll);
1152                         do_draw= 1;
1153                         break;
1154                 }
1155         }
1156
1157         if (do_draw) {
1158                 ScrArea *sa;
1159                 
1160                 for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1161                         SpaceText *st= sa->spacedata.first;
1162                         
1163                         if (st && st->spacetype==SPACE_TEXT) {
1164                                 scrarea_queue_redraw(sa);
1165                         }
1166                 }
1167         }
1168 }