Fix compilation problem in Windows and update MSVC project files
[blender.git] / source / blender / src / header_text.c
1 /**
2  * header_text.c oct-2003
3  *
4  * Functions to draw the "Text Editor" window header
5  * and handle user events sent to it.
6  * 
7  * $Id$
8  *
9  * ***** BEGIN GPL LICENSE BLOCK *****
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 59 Temple Place - Suite 330, Boston, MA        02111-1307, USA.
24  *
25  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
26  * All rights reserved.
27  *
28  * The Original Code is: all of this file.
29  *
30  * Contributor(s): none yet.
31  *
32  * ***** END GPL LICENSE BLOCK *****
33  */
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdio.h>
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include "BMF_Api.h"
44 #include "BIF_language.h"
45 #include "MEM_guardedalloc.h"
46
47 #include "BSE_headerbuttons.h"
48
49 #include "DNA_ID.h"
50 #include "DNA_screen_types.h"
51 #include "DNA_space_types.h"
52 #include "DNA_text_types.h"
53 #include "DNA_constraint_types.h"
54 #include "DNA_action_types.h"
55
56 #include "BIF_gl.h" /* for glRasterPos2i */
57 #include "BIF_drawtext.h"
58 #include "BIF_interface.h"
59 #include "BIF_resources.h"
60 #include "BIF_screen.h"
61 #include "BIF_space.h"
62 #include "BIF_toolbox.h"
63
64 #include "BKE_global.h"
65 #include "BKE_library.h"
66 #include "BKE_main.h"
67 #include "BKE_sca.h"
68 #include "BKE_text.h"
69 #include "BKE_depsgraph.h"
70
71 #include "BSE_filesel.h"
72
73 #include "BPY_extern.h"
74 #include "BPY_menus.h"
75
76 #include "blendef.h"
77 #include "mydevice.h"
78
79 #include "PIL_time.h"
80
81 /* file time checking */
82 #include <ctype.h>
83 #include <sys/types.h>
84 #include <sys/stat.h>
85
86 // INT is defined in BIF_interface.h as constant but is also a typedef in Windows
87 // This annoying problem should be fixed by using a less conflicting name in Blender.
88 #undef INT
89
90 #ifndef _WIN32
91 #include <unistd.h>
92 #else
93 #include <io.h>
94 #include "BLI_winstuff.h"
95 #endif
96
97 extern void redraw_alltext(void); /* defined in drawtext.c */
98
99 void do_text_buttons(unsigned short event)
100 {
101         SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
102         ID *id, *idtest;
103         int nr= 1;
104         Text *text;
105         
106         if (st==NULL || st->spacetype != SPACE_TEXT) return;
107         
108         switch (event) {
109         case B_TEXTBROWSE:
110                 if (st->menunr==-2) {
111                         activate_databrowse((ID *)st->text, ID_TXT, 0, B_TEXTBROWSE,
112                                                                                         &st->menunr, do_text_buttons);
113                         break;
114                 }
115                 if(st->menunr < 0) break;
116                         
117                 text= st->text;
118
119                 nr= 1;
120                 id= (ID *)text;
121                 
122                 if (st->menunr==32767) {
123                         st->text= (Text *)add_empty_text( "Text" );
124
125                         st->top= 0;
126                         
127                         allqueue(REDRAWTEXT, 0);
128                         allqueue(REDRAWHEADERS, 0); 
129                 }
130                 else if (st->menunr==32766) {
131                         activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs); 
132                         return;
133                 }
134                 else {          
135                         idtest= G.main->text.first;
136                         while(idtest) {
137                                 if(nr==st->menunr) {
138                                         break;
139                                 }
140                                 nr++;
141                                 idtest= idtest->next;
142                         }
143                         if(idtest==0) { /* new text */
144                                 activate_fileselect(FILE_SPECIAL, "Open Text File",
145                                                                                                 G.sce, add_text_fs); 
146                                 return;
147                         }
148                         if(idtest!=id) {
149                                 st->text= (Text *)idtest;
150                                 st->top= 0;
151                                 
152                                 pop_space_text(st);
153                                 if (st->showsyntax) txt_format_text(st);
154                                 allqueue(REDRAWTEXT, 0);
155                                 allqueue(REDRAWHEADERS, 0);
156                         }
157                 }
158                 break;
159                 
160         case B_TEXTDELETE:
161                 {
162                         text= st->text;
163                         if (!text) return;
164                         
165                         /* make the previous text active, if its not there make the next text active */
166                         if (st->text->id.prev) {
167                                 st->text = st->text->id.prev;
168                                 pop_space_text(st);
169                         } else if (st->text->id.next) {
170                                 st->text = st->text->id.next;
171                                 pop_space_text(st);
172                         }
173                         
174                         BPY_clear_bad_scriptlinks(text);
175                         BPY_free_pyconstraint_links(text);
176                         free_text_controllers(text);
177                         
178                         unlink_text(text);
179                         free_libblock(&G.main->text, text);
180                         
181                         allqueue(REDRAWTEXT, 0);
182                         allqueue(REDRAWHEADERS, 0);
183                         
184                         /*for if any object constraints were changed.*/
185                         allqueue(REDRAWVIEW3D, 0);
186                         allqueue(REDRAWBUTSOBJECT, 0);
187                         allqueue(REDRAWBUTSEDIT, 0);
188                         
189                         BIF_undo_push("Delete Text");
190                 }
191                 break;
192                 
193 /*
194         case B_TEXTSTORE:
195                 st->text->flags ^= TXT_ISEXT;
196                 
197                 allqueue(REDRAWHEADERS, 0);
198                 break;
199 */               
200         case B_TEXTLINENUM:
201                 allqueue(REDRAWTEXT, 0);
202                 allqueue(REDRAWHEADERS, 0);
203                 break;
204
205         case B_TEXTFONT:
206                 switch(st->font_id) {
207                 case 0:
208                         st->lheight= 12; break;
209                 case 1:
210                         st->lheight= 15; 
211                         break;
212                 }
213                         
214                 allqueue(REDRAWTEXT, 0);
215                 allqueue(REDRAWHEADERS, 0);
216
217                 break;
218         case B_TAB_NUMBERS:
219                 if (st->showsyntax) txt_format_text(st);
220                 allqueue(REDRAWTEXT, 0);
221                 allqueue(REDRAWHEADERS, 0);
222                 break;
223         case B_SYNTAX:
224                 if (st->showsyntax) txt_format_text(st);
225                 allqueue(REDRAWTEXT, 0);
226                 allqueue(REDRAWHEADERS, 0);
227                 break;
228         case B_TEXTPLUGINS:
229                 allqueue(REDRAWHEADERS, 0);
230                 break;
231         case B_WORDWRAP:
232                 st->left= 0;
233                 allqueue(REDRAWTEXT, 0);
234                 allqueue(REDRAWHEADERS, 0);
235                 break;
236         }
237 }
238
239 static void do_text_template_scriptsmenu(void *arg, int event)
240 {
241         BPY_menu_do_python(PYMENU_SCRIPTTEMPLATE, event);
242         
243         allqueue(REDRAWIMAGE, 0);
244 }
245
246 static uiBlock *text_template_scriptsmenu (void *args_unused)
247 {
248         uiBlock *block;
249         BPyMenu *pym;
250         int i= 0;
251         short yco = 20, menuwidth = 120;
252         
253         block= uiNewBlock(&curarea->uiblocks, "text_template_scriptsmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
254         uiBlockSetButmFunc(block, do_text_template_scriptsmenu, NULL);
255         
256         /* note that we acount for the N previous entries with i+20: */
257         for (pym = BPyMenuTable[PYMENU_SCRIPTTEMPLATE]; pym; pym = pym->next, i++) {
258                 
259                 uiDefIconTextBut(block, BUTM, 1, ICON_PYTHON, pym->name, 0, yco-=20, menuwidth, 19, 
260                                                  NULL, 0.0, 0.0, 1, i, 
261                                                  pym->tooltip?pym->tooltip:pym->filename);
262         }
263         
264         uiBlockSetDirection(block, UI_RIGHT);
265         uiTextBoundsBlock(block, 60);
266         
267         return block;
268 }
269
270 static void do_text_plugin_scriptsmenu(void *arg, int event)
271 {
272         BPY_menu_do_python(PYMENU_TEXTPLUGIN, event);
273         
274         allqueue(REDRAWIMAGE, 0);
275 }
276
277 static uiBlock *text_plugin_scriptsmenu (void *args_unused)
278 {
279         uiBlock *block;
280         BPyMenu *pym;
281         int i= 0;
282         short yco = 20, menuwidth = 120;
283         
284         block= uiNewBlock(&curarea->uiblocks, "text_plugin_scriptsmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
285         uiBlockSetButmFunc(block, do_text_plugin_scriptsmenu, NULL);
286         
287         /* note that we acount for the N previous entries with i+20: */
288         for (pym = BPyMenuTable[PYMENU_TEXTPLUGIN]; pym; pym = pym->next, i++) {
289                 
290                 uiDefIconTextBut(block, BUTM, 1, ICON_PYTHON, pym->name, 0, yco-=20, menuwidth, 19, 
291                                                  NULL, 0.0, 0.0, 1, i, 
292                                                  pym->tooltip?pym->tooltip:pym->filename);
293         }
294         
295         uiBlockSetDirection(block, UI_RIGHT);
296         uiTextBoundsBlock(block, 60);
297         
298         return block;
299 }
300
301 /* action executed after clicking in File menu */
302 static void do_text_filemenu(void *arg, int event)
303 {
304         SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
305         Text *text;
306         ScrArea *sa;
307         
308         if (st==NULL || st->spacetype != SPACE_TEXT) return;
309         
310         text= st->text;
311         
312         switch(event) {
313         case 1:
314                 st->text= add_empty_text( "Text" );
315                 st->top=0;
316                 
317                 allqueue(REDRAWTEXT, 0);
318                 allqueue(REDRAWHEADERS, 0);
319                 break;
320         case 2:
321                 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
322                 break;
323         case 3:
324                 if (text->compiled) BPY_free_compiled_text(text);
325                         text->compiled = NULL;
326                         if (okee("Reopen Text")) {
327                                 if (!reopen_text(text)) {
328                                         error("Could not reopen file");
329                                 }
330                                 if (st->showsyntax) txt_format_text(st);
331                         }
332                 break;
333         case 5:
334                 text->flags |= TXT_ISMEM;
335         case 4:
336                 txt_write_file(text);
337                 break;
338         case 6:
339                 text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
340                 MEM_freeN(text->name);
341                 text->name= NULL;
342                 break;
343         case 7:
344                 run_python_script(st);
345                 break;
346         case 8:
347         {
348                 Object *ob;
349                 bConstraint *con;
350                 short update;
351                 
352                 /* check all pyconstraints */
353                 for (ob= G.main->object.first; ob; ob= ob->id.next) {
354                         update = 0;
355                         if (ob->type==OB_ARMATURE && ob->pose) {
356                                 bPoseChannel *pchan;
357                                 for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
358                                         for (con = pchan->constraints.first; con; con= con->next) {
359                                                 if (con->type==CONSTRAINT_TYPE_PYTHON) {
360                                                         bPythonConstraint *data = con->data;
361                                                         if (data->text==text) BPY_pyconstraint_update(ob, con);
362                                                         update = 1;
363                                                         
364                                                 }
365                                         }
366                                 }
367                         }
368                         for (con = ob->constraints.first; con; con= con->next) {
369                                 if (con->type==CONSTRAINT_TYPE_PYTHON) {
370                                         bPythonConstraint *data = con->data;
371                                         if (data->text==text) BPY_pyconstraint_update(ob, con);
372                                         update = 1;
373                                 }
374                         }
375                         
376                         if (update) {
377                                 DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
378                         }
379                 }
380         }
381                 break;
382         default:
383                 break;
384         }
385         
386         redraw_alltext();
387 }
388
389 /* action executed after clicking in Edit menu */
390 static void do_text_editmenu(void *arg, int event)
391 {
392         SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
393         Text *text;
394         ScrArea *sa;
395         
396         if (st==NULL || st->spacetype != SPACE_TEXT) return;
397         
398         text= st->text;
399         
400         switch(event) {
401         case 1:
402                 txt_do_undo(text);
403                 pop_space_text(st);
404                 break;
405         case 2:
406                 txt_do_redo(text);
407                 pop_space_text(st);
408                 break;
409         case 3:
410                 if (text && text->id.lib) {
411                         error_libdata();
412                         break;
413                 }
414                 txt_copy_clipboard(text);
415                 txt_cut_sel(text);
416                 pop_space_text(st);
417                 break;
418         case 4:
419                 //txt_copy_sel(text);
420                 txt_copy_clipboard(text);
421                 break;
422         case 5:
423                 if (text && text->id.lib) {
424                         error_libdata();
425                         break;
426                 }
427                 txt_paste_clipboard(text);
428                 if (st->showsyntax) txt_format_text(st);
429                 break;
430         case 6:
431                 txt_print_cutbuffer();
432                 break;
433         case 7:
434                 jumptoline_interactive(st);
435                 break;
436         case 8:
437         case 9:
438                 find_and_replace(st, 0);
439                 break;
440         case 10:
441                 find_and_replace(st, 1);
442                 break;
443         default:
444                 break;
445         }
446
447         redraw_alltext();
448 }
449
450 /* action executed after clicking in View menu */
451 static void do_text_editmenu_viewmenu(void *arg, int event)
452 {
453         SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
454         Text *text;
455         ScrArea *sa;
456         
457         if (st==NULL || st->spacetype != SPACE_TEXT) return;
458         
459         text = st->text;
460         
461         switch(event) {
462                 case 1:
463                         txt_move_bof(text, 0);
464                         pop_space_text(st);
465                         break;
466                 case 2:
467                         txt_move_eof(text, 0);
468                         pop_space_text(st);
469                         break;
470                 default:
471                         break;
472         }
473         
474         redraw_alltext();
475 }
476
477 /* action executed after clicking in Select menu */
478 static void do_text_editmenu_selectmenu(void *arg, int event)
479 {
480         SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
481         Text *text;
482         ScrArea *sa;
483         
484         if (st==NULL || st->spacetype != SPACE_TEXT) return;
485         
486         text = st->text;
487         
488         switch(event) {
489         case 1:
490                 txt_sel_all(text);
491                 break;          
492         case 2:
493                 txt_sel_line(text);
494                 break;
495         default:
496                 break;
497         }
498
499         redraw_alltext();
500 }
501
502 /* action executed after clicking in Markers menu */
503 static void do_text_editmenu_markermenu(void *arg, int event)
504 {
505         SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
506         Text *text;
507         TextMarker *mrk;
508         ScrArea *sa;
509         int lineno;
510         
511         if (st==NULL || st->spacetype != SPACE_TEXT) return;
512         
513         text = st->text;
514         
515         switch(event) {
516         case 1:
517                 txt_clear_markers(text, 0, 0);
518                 break;
519         case 2:
520                 lineno= txt_get_span(text->lines.first, text->curl);
521                 mrk= text->markers.first;
522                 while (mrk && (mrk->lineno<lineno || (mrk->lineno==lineno && mrk->start <= text->curc)))
523                         mrk= mrk->next;
524                 if (!mrk) mrk= text->markers.first;
525                 if (mrk) {
526                         txt_move_to(text, mrk->lineno, mrk->start, 0);
527                         txt_move_to(text, mrk->lineno, mrk->end, 1);
528                 }
529                 break;
530         case 3:
531                 lineno= txt_get_span(text->lines.first, text->curl);
532                 mrk= text->markers.last;
533                 while (mrk && (mrk->lineno>lineno || (mrk->lineno==lineno && mrk->end > text->curc)))
534                         mrk= mrk->prev;
535                 if (!mrk) mrk= text->markers.last;
536                 if (mrk) {
537                         txt_move_to(text, mrk->lineno, mrk->start, 0);
538                         txt_move_to(text, mrk->lineno, mrk->end, 1);
539                 }
540                 break;
541         default:
542                 break;
543         }
544
545         redraw_alltext();
546 }
547
548 /* action executed after clicking in Format menu */
549 static void do_text_formatmenu(void *arg, int event)
550 {
551         SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
552         Text *text;
553         ScrArea *sa;
554         
555         if (st==NULL || st->spacetype != SPACE_TEXT) return;
556         
557         text = st->text;
558         
559         switch(event) {
560         case 3:
561                 if (text && text->id.lib) {
562                         error_libdata();
563                         break;
564                 }
565                 if (txt_has_sel(text)) {
566                         txt_order_cursors(text);
567                         indent(text);
568                         break;
569                 }
570                 else {
571                         txt_add_char(text, '\t');
572                         break;
573                 }
574         case 4:
575                 if (text && text->id.lib) {
576                         error_libdata();
577                         break;
578                 }
579                 if ( txt_has_sel(text)) {
580                         txt_order_cursors(text);
581                         unindent(text);
582                         break;
583                 }
584                 break;
585         case 5:
586                 if (text && text->id.lib) {
587                         error_libdata();
588                         break;
589                 }
590                 if ( txt_has_sel(text)) {
591                         txt_order_cursors(text);
592                         comment(text);
593                         if (st->showsyntax) txt_format_text(st);
594                         break;
595                 }
596                 break;
597         case 6:
598                 if (text && text->id.lib) {
599                         error_libdata();
600                         break;
601                 }
602                 if ( txt_has_sel(text)) {
603                         txt_order_cursors(text);
604                         uncomment(text);
605                         if (st->showsyntax) txt_format_text(st);
606                         break;
607                 }
608                 break;
609         default:
610                 break;
611         }
612
613         redraw_alltext();
614 }
615
616 /* View menu */
617 static uiBlock *text_editmenu_viewmenu(void *arg_unused)
618 {
619         uiBlock *block;
620         short yco = 20, menuwidth = 120;
621
622         block= uiNewBlock(&curarea->uiblocks, "text_editmenu_viewmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
623         uiBlockSetButmFunc(block, do_text_editmenu_viewmenu, NULL);
624
625         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Top of File", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 1, "");
626         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Bottom of File", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 2, "");
627
628         uiBlockSetDirection(block, UI_RIGHT);
629         uiTextBoundsBlock(block, 60);
630         
631         return block;
632 }
633
634 /* Select menu */
635 static uiBlock *text_editmenu_selectmenu(void *arg_unused)
636 {
637         uiBlock *block;
638         short yco = 20, menuwidth = 120;
639
640         block= uiNewBlock(&curarea->uiblocks, "text_editmenu_selectmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
641         uiBlockSetButmFunc(block, do_text_editmenu_selectmenu, NULL);
642
643         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select All|Ctrl A", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 1, "");
644         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Select Line", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 2, "");
645         
646         uiBlockSetDirection(block, UI_RIGHT);
647         uiTextBoundsBlock(block, 60);
648         
649         return block;
650 }
651
652 /* Select menu */
653 static uiBlock *text_editmenu_markermenu(void *arg_unused)
654 {
655         uiBlock *block;
656         short yco = 20, menuwidth = 120;
657
658         block= uiNewBlock(&curarea->uiblocks, "text_editmenu_markermenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
659         uiBlockSetButmFunc(block, do_text_editmenu_markermenu, NULL);
660
661         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Clear All", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 1, "");
662         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Next Marker", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 2, "");
663         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Previous Marker", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 3, "");
664         
665         uiBlockSetDirection(block, UI_RIGHT);
666         uiTextBoundsBlock(block, 60);
667         
668         return block;
669 }
670
671 void do_text_formatmenu_convert(void *arg, int event)
672 {
673         SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
674         
675         if (st==NULL || st->spacetype != SPACE_TEXT) return;
676         
677         switch(event) {
678         case 1: convert_tabs(st, 0); break;
679         case 2: convert_tabs(st, 1); break;
680         }
681         allqueue(REDRAWVIEW3D, 0);
682 }
683
684 static uiBlock *text_formatmenu_convert(void *arg_unused)
685 {
686         uiBlock *block;
687         short yco = 20, menuwidth = 120;
688
689         block= uiNewBlock(&curarea->uiblocks, "do_text_formatmenu_convert", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
690         uiBlockSetButmFunc(block, do_text_formatmenu_convert, NULL);
691
692         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "To Spaces",              0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "Converts script whitespace to spaces based on Tab:");
693         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "To Tabs",                0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "Converts script whitespace to tabs based on Tab:");
694         
695         uiBlockSetDirection(block, UI_RIGHT);
696         uiTextBoundsBlock(block, 60);
697         return block;
698 }
699
700 /* Format menu */
701 static uiBlock *text_formatmenu(void *arg_unused)
702 {
703         uiBlock *block;
704         short yco= 0, menuwidth=120;
705
706         block= uiNewBlock(&curarea->uiblocks, "text_formatmenu", UI_EMBOSSP, UI_HELV, curarea->headwin);
707         uiBlockSetButmFunc(block, do_text_formatmenu, NULL);
708
709         uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
710         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Indent|Tab", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 3, "");
711         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Unindent|Shift Tab", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 4, "");
712         uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
713         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Comment", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 5, "");
714         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Uncomment|Ctrl Shift D", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 6, "");
715         uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
716         uiDefIconTextBlockBut(block, text_formatmenu_convert, NULL, ICON_RIGHTARROW_THIN, "Convert whitespace", 0, yco-=20, menuwidth, 19, "");
717         
718         if(curarea->headertype==HEADERTOP) {
719                 uiBlockSetDirection(block, UI_DOWN);
720         }
721         else {
722                 uiBlockSetDirection(block, UI_TOP);
723                 uiBlockFlipOrder(block);
724         }
725
726         uiTextBoundsBlock(block, 50);
727         return block;
728 }
729
730
731 /* action executed after clicking in Object to 3d Sub Menu */
732 void do_text_editmenu_to3dmenu(void *arg, int event)
733 {
734         SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
735         Text *text;
736         if (st==NULL || st->spacetype != SPACE_TEXT) return;
737         
738         text = st->text;
739         
740         switch(event) {
741         case 1: txt_export_to_object(text); break;
742         case 2: txt_export_to_objects(text); break;
743         }
744         allqueue(REDRAWVIEW3D, 0);
745 }
746
747 /* Object to 3d Sub Menu */
748 static uiBlock *text_editmenu_to3dmenu(void *arg_unused)
749 {
750         uiBlock *block;
751         short yco = 20, menuwidth = 120;
752
753         block= uiNewBlock(&curarea->uiblocks, "do_text_editmenu_to3dmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
754         uiBlockSetButmFunc(block, do_text_editmenu_to3dmenu, NULL);
755
756         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "One Object | Alt-M",             0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
757         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "One Object Per Line",            0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
758         
759         uiBlockSetDirection(block, UI_RIGHT);
760         uiTextBoundsBlock(block, 60);
761         return block;
762 }
763
764
765 /* Edit menu */
766 static uiBlock *text_editmenu(void *arg_unused)
767 {
768         uiBlock *block;
769         short yco= 0, menuwidth=120;
770
771         block= uiNewBlock(&curarea->uiblocks, "text_editmenu", UI_EMBOSSP, UI_HELV, curarea->headwin);
772         uiBlockSetButmFunc(block, do_text_editmenu, NULL);
773
774         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Undo|Ctrl Z", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 1, "");
775         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Redo|Ctrl Shift Z", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 2, "");
776         uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
777         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Cut|Alt X", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 3, "");
778         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Copy|Alt C", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 4, "");
779         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Paste|Alt V", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 5, "");
780         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Print Cut Buffer", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 6, "");
781         uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
782         uiDefIconTextBlockBut(block, text_editmenu_viewmenu, NULL, ICON_RIGHTARROW_THIN, "View|Alt Shift V   ", 0, yco-=20, 120, 19, "");
783         uiDefIconTextBlockBut(block, text_editmenu_selectmenu, NULL, ICON_RIGHTARROW_THIN, "Select|Alt Shift S   ", 0, yco-=20, 120, 19, "");
784         uiDefIconTextBlockBut(block, text_editmenu_markermenu, NULL, ICON_RIGHTARROW_THIN, "Markers", 0, yco-=20, 120, 19, "");
785         uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
786         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Jump...|Alt J", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 7, "");
787         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find And Replace...|Alt F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 8, "");
788         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Find Next|Alt F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 9, "");
789         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Replace|Alt H", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 10, "");
790         uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
791         uiDefIconTextBlockBut(block, text_editmenu_to3dmenu, NULL, ICON_RIGHTARROW_THIN, "Text to 3d Object", 0, yco-=20, 120, 19, "");
792         
793         if(curarea->headertype==HEADERTOP) {
794                 uiBlockSetDirection(block, UI_DOWN);
795         }
796         else {
797                 uiBlockSetDirection(block, UI_TOP);
798                 uiBlockFlipOrder(block);
799         }
800
801         uiTextBoundsBlock(block, 50);
802         return block;
803 }
804
805 /* File menu */
806 static uiBlock *text_filemenu(void *arg_unused)
807 {
808         SpaceText *st= curarea->spacedata.first; /* bad but cant pass as an arg here */
809         Text *text= st->text;
810         uiBlock *block;
811         short yco= 0, menuwidth=120;
812
813         block= uiNewBlock(&curarea->uiblocks, "text_filemenu", UI_EMBOSSP, UI_HELV, curarea->headwin);
814         uiBlockSetButmFunc(block, do_text_filemenu, NULL);
815
816         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "New|Alt N", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 1, "");
817         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Open...|Alt O", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 2, "");
818         
819         if(text) {
820                 uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Reopen|Alt R", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 3, "");
821                 
822                 uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
823                 
824                 uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Save|Alt S", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 4, "");
825                 uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Save As...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 5, "");
826                 
827                 if (text->name)
828                         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Make Internal", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 6, "");
829
830                 uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
831                 
832                 uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Run Python Script|Alt P", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 7, "");
833                 
834                 if (BPY_is_pyconstraint(text))
835                         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Refresh All PyConstraints", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 8, "");
836                         
837                 uiDefBut(block, SEPR, 0, "",        0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
838         }
839         
840         uiDefIconTextBlockBut(block, text_template_scriptsmenu, NULL, ICON_RIGHTARROW_THIN, "Script Templates", 0, yco-=20, 120, 19, "");
841         uiDefIconTextBlockBut(block, text_plugin_scriptsmenu, NULL, ICON_RIGHTARROW_THIN, "Text Plugins", 0, yco-=20, 120, 19, "");
842
843         if(curarea->headertype==HEADERTOP) {
844                 uiBlockSetDirection(block, UI_DOWN);
845         }
846         else {
847                 uiBlockSetDirection(block, UI_TOP);
848                 uiBlockFlipOrder(block);
849         }
850
851         uiTextBoundsBlock(block, 50);
852         return block;
853 }
854
855
856 /* text sync functions */
857
858 /* returns 0 if file on disk is the same or Text is in memory only
859    returns 1 if file has been modified on disk since last local edit
860    returns 2 if file on disk has been deleted
861    -1 is returned if an error occurs
862 */
863 static int txt_file_modified(Text *text)
864 {
865         struct stat st;
866         int result;
867         char file[FILE_MAXDIR+FILE_MAXFILE];
868
869         if (!text || !text->name)
870                 return 0;
871
872         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
873         BLI_convertstringcode(file, G.sce);
874
875         if (!BLI_exists(file))
876                 return 2;
877
878         result = stat(file, &st);
879         
880         if(result == -1)
881                 return -1;
882
883         if((st.st_mode & S_IFMT) != S_IFREG)
884                 return -1;
885
886         if (st.st_mtime > text->mtime)
887                 return 1;
888
889         return 0;
890 }
891
892 static void txt_ignore_modified(Text *text) {
893         struct stat st;
894         int result;
895         char file[FILE_MAXDIR+FILE_MAXFILE];
896
897         if (!text || !text->name) return;
898
899         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
900         BLI_convertstringcode(file, G.sce);
901
902         if (!BLI_exists(file)) return;
903
904         result = stat(file, &st);
905         
906         if(result == -1 || (st.st_mode & S_IFMT) != S_IFREG)
907                 return;
908
909         text->mtime= st.st_mtime;
910 }
911
912 static double last_check_time= 0;
913
914 static short do_modification_check(SpaceText *st_v) {
915         SpaceText *st = (SpaceText *)st_v;
916         Text *text= st->text;
917
918         if (last_check_time < PIL_check_seconds_timer() - 2.0) {
919                 switch (txt_file_modified(text)) {
920                 case 1:
921                         /* Modified locally and externally, ahhh. Offer more possibilites. */
922                         if (text->flags & TXT_ISDIRTY) {
923                                 switch (pupmenu("File Modified Outside and Inside Blender %t|Load outside changes (ignore local changes) %x0|Save local changes (ignore outside changes) %x1|Make text internal (separate copy) %x2")) {
924                                 case 0:
925                                         reopen_text(text);
926                                         if (st->showsyntax) txt_format_text(st);
927                                         return 1;
928                                 case 1:
929                                         txt_write_file(text);
930                                         return 1;
931                                 case 2:
932                                         text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
933                                         MEM_freeN(text->name);
934                                         text->name= NULL;
935                                         return 1;
936                                 }
937                         } else {
938                                 switch (pupmenu("File Modified Outside Blender %t|Reload from disk %x0|Make text internal (separate copy) %x1|Ignore %x2")) {
939                                 case 0:
940                                         if (text->compiled) BPY_free_compiled_text(text);
941                                                 text->compiled = NULL;
942                                         reopen_text(text);
943                                         if (st->showsyntax) txt_format_text(st);
944                                         return 1;
945                                 case 1:
946                                         text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
947                                         MEM_freeN(text->name);
948                                         text->name= NULL;
949                                         return 1;
950                                 case 2:
951                                         txt_ignore_modified(text);
952                                         return 1;
953                                 }
954                         }
955                         break;
956                 case 2:
957                         switch (pupmenu("File Deleted Outside Blender %t|Make text internal %x0|Recreate file %x1")) {
958                         case 0:
959                                 text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
960                                 MEM_freeN(text->name);
961                                 text->name= NULL;
962                                 return 1;
963                         case 1:
964                                 txt_write_file(text);
965                                 return 1;
966                         }
967                         break;
968                 default:
969                         break;
970                 }
971                 last_check_time = PIL_check_seconds_timer();
972         }
973         return 0;
974 }
975
976 static void do_modification_func(void *st_v, void *dummy)
977 {
978         if (do_modification_check((SpaceText *)st_v))
979                 redraw_alltext();
980 }
981
982 /* header */
983 #define HEADER_PATH_MAX 260
984 void text_buttons(void)
985 {
986         uiBlock *block;
987         SpaceText *st= curarea->spacedata.first;
988         Text *text;
989         short xco, xmax;
990         char naam[256], fname[HEADER_PATH_MAX], headtxt[HEADER_PATH_MAX+17];
991         int len;
992         
993         if (st==NULL || st->spacetype != SPACE_TEXT) return;
994         
995         text = st->text;
996
997         sprintf(naam, "header %d", curarea->headwin);
998         block= uiNewBlock(&curarea->uiblocks, naam, UI_EMBOSS, UI_HELV, curarea->headwin);
999
1000         if(area_is_active_area(curarea)) uiBlockSetCol(block, TH_HEADER);
1001         else uiBlockSetCol(block, TH_HEADERDESEL);
1002
1003         curarea->butspacetype= SPACE_TEXT;
1004
1005         xco = 8;
1006         uiDefIconTextButC(block, ICONTEXTROW,B_NEWSPACE, ICON_VIEW3D, windowtype_pup(), xco,0,XIC+10,YIC, &(curarea->butspacetype), 1.0, SPACEICONMAX, 0, 0, "Displays Current Window Type. Click for menu of available types.");
1007         xco+= XIC+14;
1008
1009         uiBlockSetEmboss(block, UI_EMBOSSN);
1010         if(curarea->flag & HEADER_NO_PULLDOWN) {
1011                 uiDefIconButBitS(block, TOG, HEADER_NO_PULLDOWN, B_FLIPINFOMENU, ICON_DISCLOSURE_TRI_RIGHT,
1012                                 xco,2,XIC,YIC-2,
1013                                 &(curarea->flag), 0, 0, 0, 0, "Enables display of pulldown menus");
1014         } else {
1015                 uiDefIconButBitS(block, TOG, HEADER_NO_PULLDOWN, B_FLIPINFOMENU, ICON_DISCLOSURE_TRI_DOWN,
1016                                 xco,2,XIC,YIC-2,
1017                                 &(curarea->flag), 0, 0, 0, 0, "Hides pulldown menus");
1018         }
1019         uiBlockSetEmboss(block, UI_EMBOSS);
1020         xco+=XIC;
1021
1022         /* pull down menus */
1023         if((curarea->flag & HEADER_NO_PULLDOWN)==0) {
1024                 uiBlockSetEmboss(block, UI_EMBOSSP);
1025         
1026                 xmax= GetButStringLength("Text");
1027                 uiDefPulldownBut(block,text_filemenu, NULL, "Text", xco, 0, xmax, 20, "");
1028                 xco+=xmax;
1029         
1030                 if(text) {
1031                         xmax= GetButStringLength("Edit");
1032                         uiDefPulldownBut(block,text_editmenu, NULL, "Edit", xco, 0, xmax, 20, "");
1033                         xco+=xmax;
1034                         
1035                         xmax= GetButStringLength("Format");
1036                         uiDefPulldownBut(block,text_formatmenu, NULL, "Format", xco, 0, xmax, 20, "");
1037                         xco+=xmax;
1038                 }
1039         }
1040         uiBlockSetEmboss(block, UI_EMBOSS);
1041         xco += 10;
1042         
1043         /* FULL WINDOW */
1044         uiBlockBeginAlign(block);
1045         if(curarea->full) uiDefIconBut(block, BUT,B_FULL, ICON_SPLITSCREEN,     xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Returns to multiple views window (CTRL+Up arrow)");
1046         else uiDefIconBut(block, BUT,B_FULL, ICON_FULLSCREEN,   xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Makes current window full screen (CTRL+Down arrow)");
1047                 
1048         uiDefIconButI(block, ICONTOG, B_TEXTLINENUM, ICON_LONGDISPLAY, xco+=XIC,0,XIC,YIC, &st->showlinenrs, 0, 0, 0, 0, "Displays line numbers");
1049         uiDefIconButI(block, ICONTOG, B_WORDWRAP, ICON_WORDWRAP, xco+=XIC,0,XIC,YIC, &st->wordwrap, 0, 0, 0, 0, "Enables word wrap");
1050         uiDefIconButI(block, ICONTOG, B_SYNTAX, ICON_SYNTAX, xco+=XIC,0,XIC,YIC, &st->showsyntax, 0, 0, 0, 0, "Enables syntax highlighting");
1051         uiDefIconButI(block, ICONTOG, B_TEXTPLUGINS, ICON_PYTHON, xco+=XIC,0,XIC,YIC, &st->doplugins, 0, 0, 0, 0, "Enables Python text plugins");
1052         uiBlockEndAlign(block);
1053         
1054         /* Warning button if text is out of date*/
1055         if (text && txt_file_modified(text)) {
1056                 uiBut *bt;
1057                 xco+= XIC;
1058                 uiBlockSetCol(block, TH_REDALERT);
1059                 bt= uiDefIconBut(block, BUT, B_NOP, ICON_HELP,  xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "External text is out of sync, click for options to resolve the conflict");
1060                 uiButSetFunc(bt, do_modification_func, (void *)st, NULL);
1061                 uiBlockSetCol(block, TH_AUTO);
1062         }
1063         
1064         /* STD TEXT BUTTONS */
1065         xco+= 2*XIC;
1066         xco= std_libbuttons(block, xco, 0, 0, NULL, B_TEXTBROWSE, ID_TXT, 0, (ID*)st->text, 0, &(st->menunr), 0, 0, B_TEXTDELETE, 0, 0);
1067         xco+=XIC;
1068         
1069         /*
1070         if (st->text) {
1071                 if (st->text->flags & TXT_ISDIRTY && (st->text->flags & TXT_ISEXT || !(st->text->flags & TXT_ISMEM)))
1072                         uiDefIconBut(block, BUT,0, ICON_ERROR, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "The text has been changed");
1073                 if (st->text->flags & TXT_ISEXT) 
1074                         uiDefBut(block, BUT,B_TEXTSTORE, ICON(),        xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Stores text in project file");
1075                 else 
1076                         uiDefBut(block, BUT,B_TEXTSTORE, ICON(),        xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Disables storing of text in project file");
1077                 xco+=10;
1078         }
1079         */              
1080
1081         
1082         if(st->font_id>1) st->font_id= 0;
1083         uiDefButI(block, MENU, B_TEXTFONT, "Screen 12 %x0|Screen 15%x1", xco,0,100,YIC, &st->font_id, 0, 0, 0, 0, "Displays available fonts");
1084         xco+=110;
1085         
1086         uiDefButI(block, NUM, B_TAB_NUMBERS, "Tab:", xco, 0, XIC+50, YIC, &st->tabnumber, 2, 8, 0, 0, "Set spacing of Tab");
1087         xco+= XIC+50;
1088
1089         /* File info */
1090         if (text) {
1091                 if (text->name) {
1092                         len = strlen(text->name);
1093                         if (len > HEADER_PATH_MAX-1)
1094                                 len = HEADER_PATH_MAX-1;
1095                         strncpy(fname, text->name, len);
1096                         fname[len]='\0';
1097                         if (text->flags & TXT_ISDIRTY)
1098                                 sprintf(headtxt, "File: *%s (unsaved)", fname);
1099                         else
1100                                 sprintf(headtxt, "File: %s", fname);
1101                 } else {
1102                         sprintf(headtxt, text->id.lib?"Text: External":"Text: Internal");
1103                 }
1104                 BIF_ThemeColor(TH_MENU_TEXT);
1105                 glRasterPos2i(xco+=XIC, 5);
1106                 BMF_DrawString(G.font, headtxt);
1107                 xco += BMF_GetStringWidth(G.font, headtxt);
1108         }
1109
1110         /* always as last  */
1111         curarea->headbutlen= xco+2*XIC;
1112
1113         uiDrawBlock(block);
1114 }