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