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