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