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