fc2c0338bdfeb32467295be1be250aa5bb2194c5
[blender-staging.git] / source / blender / windowmanager / intern / wm_operators.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2007 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /** \file blender/windowmanager/intern/wm_operators.c
30  *  \ingroup wm
31  */
32
33
34 #include <float.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <stdio.h>
38 #include <stddef.h>
39 #include <assert.h>
40
41 #include "GHOST_C-api.h"
42
43 #include "MEM_guardedalloc.h"
44
45 #include "DNA_ID.h"
46 #include "DNA_object_types.h"
47 #include "DNA_screen_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_userdef_types.h"
50 #include "DNA_windowmanager_types.h"
51
52 #include "BLF_api.h"
53
54 #include "PIL_time.h"
55
56 #include "BLI_blenlib.h"
57 #include "BLI_dynstr.h" /*for WM_operator_pystring */
58 #include "BLI_math.h"
59 #include "BLI_utildefines.h"
60
61 #include "BLO_readfile.h"
62
63 #include "BKE_blender.h"
64 #include "BKE_brush.h"
65 #include "BKE_context.h"
66 #include "BKE_depsgraph.h"
67 #include "BKE_idprop.h"
68 #include "BKE_library.h"
69 #include "BKE_global.h"
70 #include "BKE_main.h"
71 #include "BKE_report.h"
72 #include "BKE_scene.h"
73 #include "BKE_screen.h" /* BKE_ST_MAXNAME */
74
75 #include "BKE_idcode.h"
76
77 #include "BIF_gl.h"
78 #include "BIF_glutil.h" /* for paint cursor */
79
80 #include "IMB_imbuf_types.h"
81
82 #include "ED_screen.h"
83 #include "ED_util.h"
84
85 #include "RNA_access.h"
86 #include "RNA_define.h"
87 #include "RNA_enum_types.h"
88
89 #include "UI_interface.h"
90 #include "UI_resources.h"
91
92 #include "WM_api.h"
93 #include "WM_types.h"
94
95 #include "wm.h"
96 #include "wm_draw.h"
97 #include "wm_event_system.h"
98 #include "wm_event_types.h"
99 #include "wm_subwindow.h"
100 #include "wm_window.h"
101
102 static ListBase global_ops= {NULL, NULL};
103
104 /* ************ operator API, exported ********** */
105
106
107 wmOperatorType *WM_operatortype_find(const char *idname, int quiet)
108 {
109         wmOperatorType *ot;
110         
111         char idname_bl[OP_MAX_TYPENAME]; // XXX, needed to support python style names without the _OT_ syntax
112         WM_operator_bl_idname(idname_bl, idname);
113
114         if (idname_bl[0]) {
115                 ot= (wmOperatorType *)BLI_findstring_ptr(&global_ops, idname_bl, offsetof(wmOperatorType, idname));
116                 if(ot) {
117                         return ot;
118                 }
119         }
120         
121         if(!quiet)
122                 printf("search for unknown operator %s, %s\n", idname_bl, idname);
123         
124         return NULL;
125 }
126
127 wmOperatorType *WM_operatortype_first(void)
128 {
129         return global_ops.first;
130 }
131
132 /* all ops in 1 list (for time being... needs evaluation later) */
133 void WM_operatortype_append(void (*opfunc)(wmOperatorType*))
134 {
135         wmOperatorType *ot;
136         
137         ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
138         ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
139         opfunc(ot);
140
141         if(ot->name==NULL) {
142                 static char dummy_name[] = "Dummy Name";
143                 fprintf(stderr, "ERROR: Operator %s has no name property!\n", ot->idname);
144                 ot->name= dummy_name;
145         }
146
147         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description:"(undocumented operator)"); // XXX All ops should have a description but for now allow them not to.
148         RNA_def_struct_identifier(ot->srna, ot->idname);
149         BLI_addtail(&global_ops, ot);
150 }
151
152 void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType*, void*), void *userdata)
153 {
154         wmOperatorType *ot;
155
156         ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
157         ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
158         opfunc(ot, userdata);
159         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description:"(undocumented operator)");
160         RNA_def_struct_identifier(ot->srna, ot->idname);
161         BLI_addtail(&global_ops, ot);
162 }
163
164 /* ********************* macro operator ******************** */
165
166 typedef struct {
167         int retval;
168 } MacroData;
169
170 static void wm_macro_start(wmOperator *op)
171 {
172         if (op->customdata == NULL) {
173                 op->customdata = MEM_callocN(sizeof(MacroData), "MacroData");
174         }
175 }
176
177 static int wm_macro_end(wmOperator *op, int retval)
178 {
179         if (retval & OPERATOR_CANCELLED) {
180                 MacroData *md = op->customdata;
181
182                 if (md->retval & OPERATOR_FINISHED) {
183                         retval |= OPERATOR_FINISHED;
184                         retval &= ~OPERATOR_CANCELLED;
185                 }
186         }
187
188         /* if modal is ending, free custom data */
189         if (retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED)) {
190                 if (op->customdata) {
191                         MEM_freeN(op->customdata);
192                         op->customdata = NULL;
193                 }
194         }
195
196         return retval;
197 }
198
199 /* macro exec only runs exec calls */
200 static int wm_macro_exec(bContext *C, wmOperator *op)
201 {
202         wmOperator *opm;
203         int retval= OPERATOR_FINISHED;
204         
205         wm_macro_start(op);
206
207         for(opm= op->macro.first; opm; opm= opm->next) {
208                 
209                 if(opm->type->exec) {
210                         retval= opm->type->exec(C, opm);
211                 
212                         if (retval & OPERATOR_FINISHED) {
213                                 MacroData *md = op->customdata;
214                                 md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
215                         } else {
216                                 break; /* operator didn't finish, end macro */
217                         }
218                 }
219         }
220         
221         return wm_macro_end(op, retval);
222 }
223
224 static int wm_macro_invoke_internal(bContext *C, wmOperator *op, wmEvent *event, wmOperator *opm)
225 {
226         int retval= OPERATOR_FINISHED;
227
228         /* start from operator received as argument */
229         for( ; opm; opm= opm->next) {
230                 if(opm->type->invoke)
231                         retval= opm->type->invoke(C, opm, event);
232                 else if(opm->type->exec)
233                         retval= opm->type->exec(C, opm);
234
235                 BLI_movelisttolist(&op->reports->list, &opm->reports->list);
236                 
237                 if (retval & OPERATOR_FINISHED) {
238                         MacroData *md = op->customdata;
239                         md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
240                 } else {
241                         break; /* operator didn't finish, end macro */
242                 }
243         }
244
245         return wm_macro_end(op, retval);
246 }
247
248 static int wm_macro_invoke(bContext *C, wmOperator *op, wmEvent *event)
249 {
250         wm_macro_start(op);
251         return wm_macro_invoke_internal(C, op, event, op->macro.first);
252 }
253
254 static int wm_macro_modal(bContext *C, wmOperator *op, wmEvent *event)
255 {
256         wmOperator *opm = op->opm;
257         int retval= OPERATOR_FINISHED;
258         
259         if(opm==NULL)
260                 printf("macro error, calling NULL modal()\n");
261         else {
262                 retval = opm->type->modal(C, opm, event);
263
264                 /* if this one is done but it's not the last operator in the macro */
265                 if ((retval & OPERATOR_FINISHED) && opm->next) {
266                         MacroData *md = op->customdata;
267
268                         md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
269
270                         retval = wm_macro_invoke_internal(C, op, event, opm->next);
271
272                         /* if new operator is modal and also added its own handler */
273                         if (retval & OPERATOR_RUNNING_MODAL && op->opm != opm) {
274                                 wmWindow *win = CTX_wm_window(C);
275                                 wmEventHandler *handler = NULL;
276
277                                 for (handler = win->modalhandlers.first; handler; handler = handler->next) {
278                                         /* first handler in list is the new one */
279                                         if (handler->op == op)
280                                                 break;
281                                 }
282
283                                 if (handler) {
284                                         BLI_remlink(&win->modalhandlers, handler);
285                                         wm_event_free_handler(handler);
286                                 }
287
288                                 /* if operator is blocking, grab cursor
289                                  * This may end up grabbing twice, but we don't care.
290                                  * */
291                                 if(op->opm->type->flag & OPTYPE_BLOCKING) {
292                                         int bounds[4] = {-1,-1,-1,-1};
293                                         int wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
294
295                                         if(wrap) {
296                                                 ARegion *ar= CTX_wm_region(C);
297                                                 if(ar) {
298                                                         bounds[0]= ar->winrct.xmin;
299                                                         bounds[1]= ar->winrct.ymax;
300                                                         bounds[2]= ar->winrct.xmax;
301                                                         bounds[3]= ar->winrct.ymin;
302                                                 }
303                                         }
304
305                                         WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
306                                 }
307                         }
308                 }
309         }
310
311         return wm_macro_end(op, retval);
312 }
313
314 static int wm_macro_cancel(bContext *C, wmOperator *op)
315 {
316         /* call cancel on the current modal operator, if any */
317         if (op->opm && op->opm->type->cancel) {
318                 op->opm->type->cancel(C, op->opm);
319         }
320
321         return wm_macro_end(op, OPERATOR_CANCELLED);
322 }
323
324 /* Names have to be static for now */
325 wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *name, int flag)
326 {
327         wmOperatorType *ot;
328         
329         if(WM_operatortype_find(idname, TRUE)) {
330                 printf("Macro error: operator %s exists\n", idname);
331                 return NULL;
332         }
333         
334         ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
335         ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
336         
337         ot->idname= idname;
338         ot->name= name;
339         ot->flag= OPTYPE_MACRO|flag;
340         
341         ot->exec= wm_macro_exec;
342         ot->invoke= wm_macro_invoke;
343         ot->modal= wm_macro_modal;
344         ot->cancel= wm_macro_cancel;
345         ot->poll= NULL;
346
347         if(!ot->description)
348                 ot->description= "(undocumented operator)";
349         
350         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description); // XXX All ops should have a description but for now allow them not to.
351         RNA_def_struct_identifier(ot->srna, ot->idname);
352
353         BLI_addtail(&global_ops, ot);
354
355         return ot;
356 }
357
358 void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType*, void*), void *userdata)
359 {
360         wmOperatorType *ot;
361
362         ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
363         ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
364
365         ot->flag= OPTYPE_MACRO;
366         ot->exec= wm_macro_exec;
367         ot->invoke= wm_macro_invoke;
368         ot->modal= wm_macro_modal;
369         ot->cancel= wm_macro_cancel;
370         ot->poll= NULL;
371
372         if(!ot->description)
373                 ot->description= "(undocumented operator)";
374
375         opfunc(ot, userdata);
376
377         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
378         RNA_def_struct_identifier(ot->srna, ot->idname);
379
380         BLI_addtail(&global_ops, ot);
381 }
382
383 wmOperatorTypeMacro *WM_operatortype_macro_define(wmOperatorType *ot, const char *idname)
384 {
385         wmOperatorTypeMacro *otmacro= MEM_callocN(sizeof(wmOperatorTypeMacro), "wmOperatorTypeMacro");
386
387         BLI_strncpy(otmacro->idname, idname, OP_MAX_TYPENAME);
388
389         /* do this on first use, since operatordefinitions might have been not done yet */
390         WM_operator_properties_alloc(&(otmacro->ptr), &(otmacro->properties), idname);
391         WM_operator_properties_sanitize(otmacro->ptr, 1);
392
393         BLI_addtail(&ot->macro, otmacro);
394
395         {
396                 /* operator should always be found but in the event its not. dont segfault */
397                 wmOperatorType *otsub = WM_operatortype_find(idname, 0);
398                 if(otsub) {
399                         RNA_def_pointer_runtime(ot->srna, otsub->idname, otsub->srna,
400                         otsub->name, otsub->description);
401                 }
402         }
403
404         return otmacro;
405 }
406
407 static void wm_operatortype_free_macro(wmOperatorType *ot)
408 {
409         wmOperatorTypeMacro *otmacro;
410         
411         for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
412                 if(otmacro->ptr) {
413                         WM_operator_properties_free(otmacro->ptr);
414                         MEM_freeN(otmacro->ptr);
415                 }
416         }
417         BLI_freelistN(&ot->macro);
418 }
419
420
421 int WM_operatortype_remove(const char *idname)
422 {
423         wmOperatorType *ot = WM_operatortype_find(idname, 0);
424
425         if (ot==NULL)
426                 return 0;
427         
428         BLI_remlink(&global_ops, ot);
429         RNA_struct_free(&BLENDER_RNA, ot->srna);
430         
431         if(ot->macro.first)
432                 wm_operatortype_free_macro(ot);
433         
434         MEM_freeN(ot);
435
436         return 1;
437 }
438
439 /* SOME_OT_op -> some.op */
440 void WM_operator_py_idname(char *to, const char *from)
441 {
442         char *sep= strstr(from, "_OT_");
443         if(sep) {
444                 int i, ofs= (sep-from);
445
446                 for(i=0; i<ofs; i++)
447                         to[i]= tolower(from[i]);
448
449                 to[ofs] = '.';
450                 BLI_strncpy(to+(ofs+1), sep+4, OP_MAX_TYPENAME);
451         }
452         else {
453                 /* should not happen but support just incase */
454                 BLI_strncpy(to, from, OP_MAX_TYPENAME);
455         }
456 }
457
458 /* some.op -> SOME_OT_op */
459 void WM_operator_bl_idname(char *to, const char *from)
460 {
461         if (from) {
462                 char *sep= strchr(from, '.');
463
464                 if(sep) {
465                         int i, ofs= (sep-from);
466
467                         for(i=0; i<ofs; i++)
468                                 to[i]= toupper(from[i]);
469
470                         BLI_strncpy(to+ofs, "_OT_", OP_MAX_TYPENAME);
471                         BLI_strncpy(to+(ofs+4), sep+1, OP_MAX_TYPENAME);
472                 }
473                 else {
474                         /* should not happen but support just incase */
475                         BLI_strncpy(to, from, OP_MAX_TYPENAME);
476                 }
477         }
478         else
479                 to[0]= 0;
480 }
481
482 /* print a string representation of the operator, with the args that it runs 
483  * so python can run it again,
484  *
485  * When calling from an existing wmOperator do.
486  * WM_operator_pystring(op->type, op->ptr);
487  */
488 char *WM_operator_pystring(bContext *C, wmOperatorType *ot, PointerRNA *opptr, int all_args)
489 {
490         const char *arg_name= NULL;
491         char idname_py[OP_MAX_TYPENAME];
492
493         PropertyRNA *prop, *iterprop;
494
495         /* for building the string */
496         DynStr *dynstr= BLI_dynstr_new();
497         char *cstring, *buf;
498         int first_iter=1, ok= 1;
499
500
501         /* only to get the orginal props for comparisons */
502         PointerRNA opptr_default;
503         PropertyRNA *prop_default;
504         char *buf_default;
505         if(all_args==0 || opptr==NULL) {
506                 WM_operator_properties_create_ptr(&opptr_default, ot);
507
508                 if(opptr==NULL)
509                         opptr = &opptr_default;
510         }
511
512
513         WM_operator_py_idname(idname_py, ot->idname);
514         BLI_dynstr_appendf(dynstr, "bpy.ops.%s(", idname_py);
515
516         iterprop= RNA_struct_iterator_property(opptr->type);
517
518         RNA_PROP_BEGIN(opptr, propptr, iterprop) {
519                 prop= propptr.data;
520                 arg_name= RNA_property_identifier(prop);
521
522                 if (strcmp(arg_name, "rna_type")==0) continue;
523
524                 buf= RNA_property_as_string(C, opptr, prop);
525                 
526                 ok= 1;
527
528                 if(!all_args) {
529                         /* not verbose, so only add in attributes that use non-default values
530                          * slow but good for tooltips */
531                         prop_default= RNA_struct_find_property(&opptr_default, arg_name);
532
533                         if(prop_default) {
534                                 buf_default= RNA_property_as_string(C, &opptr_default, prop_default);
535
536                                 if(strcmp(buf, buf_default)==0)
537                                         ok= 0; /* values match, dont bother printing */
538
539                                 MEM_freeN(buf_default);
540                         }
541
542                 }
543                 if(ok) {
544                         BLI_dynstr_appendf(dynstr, first_iter?"%s=%s":", %s=%s", arg_name, buf);
545                         first_iter = 0;
546                 }
547
548                 MEM_freeN(buf);
549
550         }
551         RNA_PROP_END;
552
553         if(all_args==0 || opptr==&opptr_default )
554                 WM_operator_properties_free(&opptr_default);
555
556         BLI_dynstr_append(dynstr, ")");
557
558         cstring = BLI_dynstr_get_cstring(dynstr);
559         BLI_dynstr_free(dynstr);
560         return cstring;
561 }
562
563 void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
564 {
565         RNA_pointer_create(NULL, ot->srna, NULL, ptr);
566 }
567
568 void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
569 {
570         wmOperatorType *ot= WM_operatortype_find(opstring, 0);
571
572         if(ot)
573                 WM_operator_properties_create_ptr(ptr, ot);
574         else
575                 RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
576 }
577
578 /* similar to the function above except its uses ID properties
579  * used for keymaps and macros */
580 void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
581 {
582         if(*properties==NULL) {
583                 IDPropertyTemplate val = {0};
584                 *properties= IDP_New(IDP_GROUP, val, "wmOpItemProp");
585         }
586
587         if(*ptr==NULL) {
588                 *ptr= MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
589                 WM_operator_properties_create(*ptr, opstring);
590         }
591
592         (*ptr)->data= *properties;
593
594 }
595
596 void WM_operator_properties_sanitize(PointerRNA *ptr, const short no_context)
597 {
598         RNA_STRUCT_BEGIN(ptr, prop) {
599                 switch(RNA_property_type(prop)) {
600                 case PROP_ENUM:
601                         if (no_context)
602                                 RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
603                         else
604                                 RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
605                         break;
606                 case PROP_POINTER:
607                         {
608                                 StructRNA *ptype= RNA_property_pointer_type(ptr, prop);
609
610                                 /* recurse into operator properties */
611                                 if (RNA_struct_is_a(ptype, &RNA_OperatorProperties)) {
612                                         PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
613                                         WM_operator_properties_sanitize(&opptr, no_context);
614                                 }
615                                 break;
616                         }
617                 default:
618                         break;
619                 }
620         }
621         RNA_STRUCT_END;
622 }
623
624 void WM_operator_properties_free(PointerRNA *ptr)
625 {
626         IDProperty *properties= ptr->data;
627
628         if(properties) {
629                 IDP_FreeProperty(properties);
630                 MEM_freeN(properties);
631                 ptr->data= NULL; /* just incase */
632         }
633 }
634
635 /* ************ default op callbacks, exported *********** */
636
637 /* invoke callback, uses enum property named "type" */
638 int WM_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
639 {
640         PropertyRNA *prop= op->type->prop;
641         uiPopupMenu *pup;
642         uiLayout *layout;
643
644         if(prop==NULL) {
645                 printf("WM_menu_invoke: %s has no enum property set\n", op->type->idname);
646         }
647         else if (RNA_property_type(prop) != PROP_ENUM) {
648                 printf("WM_menu_invoke: %s \"%s\" is not an enum property\n", op->type->idname, RNA_property_identifier(prop));
649         }
650         else if (RNA_property_is_set(op->ptr, RNA_property_identifier(prop))) {
651                 return op->type->exec(C, op);
652         }
653         else {
654                 pup= uiPupMenuBegin(C, op->type->name, ICON_NONE);
655                 layout= uiPupMenuLayout(pup);
656                 uiItemsFullEnumO(layout, op->type->idname, (char*)RNA_property_identifier(prop), op->ptr->data, WM_OP_EXEC_REGION_WIN, 0);
657                 uiPupMenuEnd(C, pup);
658         }
659
660         return OPERATOR_CANCELLED;
661 }
662
663
664 /* generic enum search invoke popup */
665 static void operator_enum_search_cb(const struct bContext *C, void *arg_ot, const char *str, uiSearchItems *items)
666 {
667         wmOperatorType *ot = (wmOperatorType *)arg_ot;
668         PropertyRNA *prop= ot->prop;
669
670         if(prop==NULL) {
671                 printf("WM_enum_search_invoke: %s has no enum property set\n", ot->idname);
672         }
673         else if (RNA_property_type(prop) != PROP_ENUM) {
674                 printf("WM_enum_search_invoke: %s \"%s\" is not an enum property\n", ot->idname, RNA_property_identifier(prop));
675         }
676         else {
677                 PointerRNA ptr;
678
679                 EnumPropertyItem *item, *item_array;
680                 int do_free;
681
682                 RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
683                 RNA_property_enum_items((bContext *)C, &ptr, prop, &item_array, NULL, &do_free);
684
685                 for(item= item_array; item->identifier; item++) {
686                         /* note: need to give the intex rather then the dientifier because the enum can be freed */
687                         if(BLI_strcasestr(item->name, str))
688                                 if(0==uiSearchItemAdd(items, item->name, SET_INT_IN_POINTER(item->value), 0))
689                                         break;
690                 }
691
692                 if(do_free)
693                         MEM_freeN(item_array);
694         }
695 }
696
697 static void operator_enum_call_cb(struct bContext *C, void *arg1, void *arg2)
698 {
699         wmOperatorType *ot= arg1;
700
701         if(ot) {
702                 if(ot->prop) {
703                         PointerRNA props_ptr;
704                         WM_operator_properties_create_ptr(&props_ptr, ot);
705                         RNA_property_enum_set(&props_ptr, ot->prop, GET_INT_FROM_POINTER(arg2));
706                         WM_operator_name_call(C, ot->idname, WM_OP_EXEC_DEFAULT, &props_ptr);
707                         WM_operator_properties_free(&props_ptr);
708                 }
709                 else {
710                         printf("operator_enum_call_cb: op->prop for '%s' is NULL\n", ot->idname);
711                 }
712         }
713 }
714
715 static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op)
716 {
717         static char search[256]= "";
718         wmEvent event;
719         wmWindow *win= CTX_wm_window(C);
720         uiBlock *block;
721         uiBut *but;
722         wmOperator *op= (wmOperator *)arg_op;
723
724         block= uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
725         uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
726
727         //uiDefBut(block, LABEL, 0, op->type->name, 10, 10, 180, 19, NULL, 0.0, 0.0, 0, 0, ""); // ok, this isnt so easy...
728         but= uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, 256, 10, 10, 180, 19, 0, 0, "");
729         uiButSetSearchFunc(but, operator_enum_search_cb, op->type, operator_enum_call_cb, NULL);
730
731         /* fake button, it holds space for search items */
732         uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxhHeight(), 180, uiSearchBoxhHeight(), NULL, 0, 0, 0, 0, NULL);
733
734         uiPopupBoundsBlock(block, 6, 0, -20); /* move it downwards, mouse over button */
735         uiEndBlock(C, block);
736
737         event= *(win->eventstate);      /* XXX huh huh? make api call */
738         event.type= EVT_BUT_OPEN;
739         event.val= KM_PRESS;
740         event.customdata= but;
741         event.customdatafree= FALSE;
742         wm_event_add(win, &event);
743
744         return block;
745 }
746
747
748 int WM_enum_search_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
749 {
750         uiPupBlock(C, wm_enum_search_menu, op);
751         return OPERATOR_CANCELLED;
752 }
753
754 /* Can't be used as an invoke directly, needs message arg (can be NULL) */
755 int WM_operator_confirm_message(bContext *C, wmOperator *op, const char *message)
756 {
757         uiPopupMenu *pup;
758         uiLayout *layout;
759         IDProperty *properties= op->ptr->data;
760
761         if(properties && properties->len)
762                 properties= IDP_CopyProperty(op->ptr->data);
763         else
764                 properties= NULL;
765
766         pup= uiPupMenuBegin(C, "OK?", ICON_QUESTION);
767         layout= uiPupMenuLayout(pup);
768         uiItemFullO(layout, op->type->idname, message, ICON_NONE, properties, WM_OP_EXEC_REGION_WIN, 0);
769         uiPupMenuEnd(C, pup);
770         
771         return OPERATOR_CANCELLED;
772 }
773
774
775 int WM_operator_confirm(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
776 {
777         return WM_operator_confirm_message(C, op, NULL);
778 }
779
780 /* op->invoke, opens fileselect if path property not set, otherwise executes */
781 int WM_operator_filesel(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
782 {
783         if (RNA_property_is_set(op->ptr, "filepath")) {
784                 return WM_operator_call(C, op);
785         } 
786         else {
787                 WM_event_add_fileselect(C, op);
788                 return OPERATOR_RUNNING_MODAL;
789         }
790 }
791
792 /* default properties for fileselect */
793 void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type, short action, short flag)
794 {
795         PropertyRNA *prop;
796
797
798         if(flag & WM_FILESEL_FILEPATH)
799                 RNA_def_string_file_path(ot->srna, "filepath", "", FILE_MAX, "File Path", "Path to file");
800
801         if(flag & WM_FILESEL_DIRECTORY)
802                 RNA_def_string_dir_path(ot->srna, "directory", "", FILE_MAX, "Directory", "Directory of the file");
803
804         if(flag & WM_FILESEL_FILENAME)
805                 RNA_def_string_file_name(ot->srna, "filename", "", FILE_MAX, "File Name", "Name of the file");
806
807         if (action == FILE_SAVE) {
808                 prop= RNA_def_boolean(ot->srna, "check_existing", 1, "Check Existing", "Check and warn on overwriting existing files");
809                 RNA_def_property_flag(prop, PROP_HIDDEN);
810         }
811         
812         prop= RNA_def_boolean(ot->srna, "filter_blender", (filter & BLENDERFILE), "Filter .blend files", "");
813         RNA_def_property_flag(prop, PROP_HIDDEN);
814         prop= RNA_def_boolean(ot->srna, "filter_image", (filter & IMAGEFILE), "Filter image files", "");
815         RNA_def_property_flag(prop, PROP_HIDDEN);
816         prop= RNA_def_boolean(ot->srna, "filter_movie", (filter & MOVIEFILE), "Filter movie files", "");
817         RNA_def_property_flag(prop, PROP_HIDDEN);
818         prop= RNA_def_boolean(ot->srna, "filter_python", (filter & PYSCRIPTFILE), "Filter python files", "");
819         RNA_def_property_flag(prop, PROP_HIDDEN);
820         prop= RNA_def_boolean(ot->srna, "filter_font", (filter & FTFONTFILE), "Filter font files", "");
821         RNA_def_property_flag(prop, PROP_HIDDEN);
822         prop= RNA_def_boolean(ot->srna, "filter_sound", (filter & SOUNDFILE), "Filter sound files", "");
823         RNA_def_property_flag(prop, PROP_HIDDEN);
824         prop= RNA_def_boolean(ot->srna, "filter_text", (filter & TEXTFILE), "Filter text files", "");
825         RNA_def_property_flag(prop, PROP_HIDDEN);
826         prop= RNA_def_boolean(ot->srna, "filter_btx", (filter & BTXFILE), "Filter btx files", "");
827         RNA_def_property_flag(prop, PROP_HIDDEN);
828         prop= RNA_def_boolean(ot->srna, "filter_collada", (filter & COLLADAFILE), "Filter COLLADA files", "");
829         RNA_def_property_flag(prop, PROP_HIDDEN);
830         prop= RNA_def_boolean(ot->srna, "filter_folder", (filter & FOLDERFILE), "Filter folders", "");
831         RNA_def_property_flag(prop, PROP_HIDDEN);
832
833         prop= RNA_def_int(ot->srna, "filemode", type, FILE_LOADLIB, FILE_SPECIAL, 
834                 "File Browser Mode", "The setting for the file browser mode to load a .blend file, a library or a special file",
835                 FILE_LOADLIB, FILE_SPECIAL);
836         RNA_def_property_flag(prop, PROP_HIDDEN);
837
838         if(flag & WM_FILESEL_RELPATH)
839                 RNA_def_boolean(ot->srna, "relative_path", (U.flag & USER_RELPATHS) ? 1:0, "Relative Path", "Select the file relative to the blend file");
840 }
841
842 void WM_operator_properties_select_all(wmOperatorType *ot) {
843         static EnumPropertyItem select_all_actions[] = {
844                         {SEL_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle selection for all elements"},
845                         {SEL_SELECT, "SELECT", 0, "Select", "Select all elements"},
846                         {SEL_DESELECT, "DESELECT", 0, "Deselect", "Deselect all elements"},
847                         {SEL_INVERT, "INVERT", 0, "Invert", "Invert selection of all elements"},
848                         {0, NULL, 0, NULL, NULL}
849         };
850
851         RNA_def_enum(ot->srna, "action", select_all_actions, SEL_TOGGLE, "Action", "Selection action to execute");
852 }
853
854 void WM_operator_properties_gesture_border(wmOperatorType *ot, int extend)
855 {
856         RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
857         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
858         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
859         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
860         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
861
862         if(extend)
863                 RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
864 }
865
866 void WM_operator_properties_gesture_straightline(wmOperatorType *ot, int cursor)
867 {
868         RNA_def_int(ot->srna, "xstart", 0, INT_MIN, INT_MAX, "X Start", "", INT_MIN, INT_MAX);
869         RNA_def_int(ot->srna, "xend", 0, INT_MIN, INT_MAX, "X End", "", INT_MIN, INT_MAX);
870         RNA_def_int(ot->srna, "ystart", 0, INT_MIN, INT_MAX, "Y Start", "", INT_MIN, INT_MAX);
871         RNA_def_int(ot->srna, "yend", 0, INT_MIN, INT_MAX, "Y End", "", INT_MIN, INT_MAX);
872         
873         if(cursor)
874                 RNA_def_int(ot->srna, "cursor", cursor, 0, INT_MAX, "Cursor", "Mouse cursor style to use during the modal operator", 0, INT_MAX);
875 }
876
877
878 /* op->poll */
879 int WM_operator_winactive(bContext *C)
880 {
881         if(CTX_wm_window(C)==NULL) return 0;
882         return 1;
883 }
884
885 wmOperator *WM_operator_last_redo(const bContext *C)
886 {
887         wmWindowManager *wm= CTX_wm_manager(C);
888         wmOperator *op;
889
890         /* only for operators that are registered and did an undo push */
891         for(op= wm->operators.last; op; op= op->prev)
892                 if((op->type->flag & OPTYPE_REGISTER) && (op->type->flag & OPTYPE_UNDO))
893                         break;
894
895         return op;
896 }
897
898 static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op)
899 {
900         wmOperator *op= arg_op;
901         uiBlock *block;
902         uiLayout *layout;
903         uiStyle *style= U.uistyles.first;
904         int width= 300;
905         
906
907         block= uiBeginBlock(C, ar, "redo_popup", UI_EMBOSS);
908         uiBlockClearFlag(block, UI_BLOCK_LOOP);
909         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
910
911         /* if register is not enabled, the operator gets freed on OPERATOR_FINISHED
912          * ui_apply_but_funcs_after calls ED_undo_operator_repeate_cb and crashes */
913         assert(op->type->flag & OPTYPE_REGISTER);
914
915         uiBlockSetHandleFunc(block, ED_undo_operator_repeat_cb_evt, arg_op);
916         layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, width, 20, style);
917
918         if(ED_undo_valid(C, op->type->name)==0)
919                 uiLayoutSetEnabled(layout, 0);
920
921         uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
922
923         uiPopupBoundsBlock(block, 4, 0, 0);
924         uiEndBlock(C, block);
925
926         return block;
927 }
928
929 /* Only invoked by OK button in popups created with wm_block_create_dialog() */
930 static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
931 {
932         wmOperator *op= arg1;
933         uiBlock *block= arg2;
934
935         WM_operator_call(C, op);
936
937         uiPupBlockClose(C, block);
938 }
939
940 static void dialog_check_cb(bContext *C, void *op_ptr, void *UNUSED(arg))
941 {
942         wmOperator *op= op_ptr;
943         if(op->type->check) {
944                 if(op->type->check(C, op)) {
945                         /* refresh */
946                 }
947         }
948 }
949
950 /* Dialogs are popups that require user verification (click OK) before exec */
951 static uiBlock *wm_block_create_dialog(bContext *C, ARegion *ar, void *userData)
952 {
953         struct { wmOperator *op; int width; int height; } * data = userData;
954         wmOperator *op= data->op;
955         uiBlock *block;
956         uiLayout *layout;
957         uiStyle *style= U.uistyles.first;
958
959         block = uiBeginBlock(C, ar, "operator dialog", UI_EMBOSS);
960         uiBlockClearFlag(block, UI_BLOCK_LOOP);
961         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
962
963         layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, style);
964         
965         uiBlockSetFunc(block, dialog_check_cb, op, NULL);
966
967         uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
968         
969         /* clear so the OK button is left alone */
970         uiBlockSetFunc(block, NULL, NULL, NULL);
971
972         /* new column so as not to interfear with custom layouts [#26436] */
973         {
974                 uiBlock *col_block;
975                 uiLayout *col;
976                 uiBut *btn;
977
978                 col= uiLayoutColumn(layout, FALSE);
979                 col_block= uiLayoutGetBlock(col);
980                 /* Create OK button, the callback of which will execute op */
981                 btn= uiDefBut(col_block, BUT, 0, "OK", 0, -30, 0, 20, NULL, 0, 0, 0, 0, "");
982                 uiButSetFunc(btn, dialog_exec_cb, op, col_block);
983         }
984
985         /* center around the mouse */
986         uiPopupBoundsBlock(block, 4, data->width/-2, data->height/2);
987         uiEndBlock(C, block);
988
989         return block;
990 }
991
992 static uiBlock *wm_operator_create_ui(bContext *C, ARegion *ar, void *userData)
993 {
994         struct { wmOperator *op; int width; int height; } * data = userData;
995         wmOperator *op= data->op;
996         uiBlock *block;
997         uiLayout *layout;
998         uiStyle *style= U.uistyles.first;
999
1000         block= uiBeginBlock(C, ar, "opui_popup", UI_EMBOSS);
1001         uiBlockClearFlag(block, UI_BLOCK_LOOP);
1002         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
1003
1004         layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, style);
1005
1006         /* since ui is defined the auto-layout args are not used */
1007         uiLayoutOperatorButs(C, layout, op, NULL, 'V', 0);
1008
1009         uiPopupBoundsBlock(block, 4, 0, 0);
1010         uiEndBlock(C, block);
1011
1012         return block;
1013 }
1014
1015 /* operator menu needs undo, for redo callback */
1016 int WM_operator_props_popup(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1017 {
1018         
1019         if((op->type->flag & OPTYPE_REGISTER)==0) {
1020                 BKE_reportf(op->reports, RPT_ERROR, "Operator '%s' does not have register enabled, incorrect invoke function.", op->type->idname);
1021                 return OPERATOR_CANCELLED;
1022         }
1023         
1024         ED_undo_push_op(C, op);
1025         wm_operator_register(C, op);
1026
1027         uiPupBlock(C, wm_block_create_redo, op);
1028
1029         return OPERATOR_RUNNING_MODAL;
1030 }
1031
1032 int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, int height)
1033 {
1034         struct { wmOperator *op; int width; int height; } data;
1035         
1036         data.op= op;
1037         data.width= width;
1038         data.height= height;
1039
1040         /* op is not executed until popup OK but is clicked */
1041         uiPupBlock(C, wm_block_create_dialog, &data);
1042
1043         return OPERATOR_RUNNING_MODAL;
1044 }
1045
1046 int WM_operator_ui_popup(bContext *C, wmOperator *op, int width, int height)
1047 {
1048         struct { wmOperator *op; int width; int height; } data;
1049         data.op = op;
1050         data.width = width;
1051         data.height = height;
1052         uiPupBlock(C, wm_operator_create_ui, &data);
1053         return OPERATOR_RUNNING_MODAL;
1054 }
1055
1056 int WM_operator_redo_popup(bContext *C, wmOperator *op)
1057 {
1058         /* CTX_wm_reports(C) because operator is on stack, not active in event system */
1059         if((op->type->flag & OPTYPE_REGISTER)==0) {
1060                 BKE_reportf(CTX_wm_reports(C), RPT_ERROR, "Operator redo '%s' does not have register enabled, incorrect invoke function.", op->type->idname);
1061                 return OPERATOR_CANCELLED;
1062         }
1063         if(op->type->poll && op->type->poll(C)==0) {
1064                 BKE_reportf(CTX_wm_reports(C), RPT_ERROR, "Operator redo '%s': wrong context.", op->type->idname);
1065                 return OPERATOR_CANCELLED;
1066         }
1067         
1068         uiPupBlock(C, wm_block_create_redo, op);
1069
1070         return OPERATOR_CANCELLED;
1071 }
1072
1073 /* ***************** Debug menu ************************* */
1074
1075 static int wm_debug_menu_exec(bContext *C, wmOperator *op)
1076 {
1077         G.rt= RNA_int_get(op->ptr, "debug_value");
1078         ED_screen_refresh(CTX_wm_manager(C), CTX_wm_window(C));
1079         WM_event_add_notifier(C, NC_WINDOW, NULL);
1080
1081         return OPERATOR_FINISHED;       
1082 }
1083
1084 static int wm_debug_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1085 {
1086         RNA_int_set(op->ptr, "debug_value", G.rt);
1087         return WM_operator_props_dialog_popup(C, op, 180, 20);
1088 }
1089
1090 static void WM_OT_debug_menu(wmOperatorType *ot)
1091 {
1092         ot->name= "Debug Menu";
1093         ot->idname= "WM_OT_debug_menu";
1094         ot->description= "Open a popup to set the debug level";
1095         
1096         ot->invoke= wm_debug_menu_invoke;
1097         ot->exec= wm_debug_menu_exec;
1098         ot->poll= WM_operator_winactive;
1099         
1100         RNA_def_int(ot->srna, "debug_value", 0, -10000, 10000, "Debug Value", "", INT_MIN, INT_MAX);
1101 }
1102
1103
1104 /* ***************** Splash Screen ************************* */
1105
1106 static void wm_block_splash_close(bContext *C, void *arg_block, void *UNUSED(arg))
1107 {
1108         uiPupBlockClose(C, arg_block);
1109 }
1110
1111 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *arg_unused);
1112
1113 /* XXX: hack to refresh splash screen with updated prest menu name,
1114  * since popup blocks don't get regenerated like panels do */
1115 static void wm_block_splash_refreshmenu (bContext *UNUSED(C), void *UNUSED(arg_block), void *UNUSED(arg))
1116 {
1117         /* ugh, causes crashes in other buttons, disabling for now until 
1118          * a better fix
1119         uiPupBlockClose(C, arg_block);
1120         uiPupBlock(C, wm_block_create_splash, NULL);
1121           */
1122 }
1123
1124 static int wm_resource_check_prev(void)
1125 {
1126
1127         char *res= BLI_get_folder_version(BLENDER_RESOURCE_PATH_USER, BLENDER_VERSION, TRUE);
1128
1129         // if(res) printf("USER: %s\n", res);
1130
1131 #if 0 /* ignore the local folder */
1132         if(res == NULL) {
1133                 /* with a local dir, copying old files isnt useful since local dir get priority for config */
1134                 res= BLI_get_folder_version(BLENDER_RESOURCE_PATH_LOCAL, BLENDER_VERSION, TRUE);
1135         }
1136 #endif
1137
1138         // if(res) printf("LOCAL: %s\n", res);
1139         if(res) {
1140                 return FALSE;
1141         }
1142         else {
1143                 return (BLI_get_folder_version(BLENDER_RESOURCE_PATH_USER, BLENDER_VERSION - 1, TRUE) != NULL);
1144         }
1145 }
1146
1147 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(arg))
1148 {
1149         uiBlock *block;
1150         uiBut *but;
1151         uiLayout *layout, *split, *col;
1152         uiStyle *style= U.uistyles.first;
1153         struct RecentFile *recent;
1154         int i;
1155         MenuType *mt= WM_menutype_find("USERPREF_MT_splash", TRUE);
1156         char url[96];
1157         
1158 #ifdef NAN_BUILDINFO
1159         int ver_width, rev_width;
1160         char *version_str = NULL;
1161         char *revision_str = NULL;
1162         char version_buf[128];
1163         char revision_buf[128];
1164         extern char build_rev[];
1165         
1166         version_str = &version_buf[0];
1167         revision_str = &revision_buf[0];
1168         
1169         sprintf(version_str, "%d.%02d.%d", BLENDER_VERSION/100, BLENDER_VERSION%100, BLENDER_SUBVERSION);
1170         sprintf(revision_str, "r%s", build_rev);
1171         
1172         BLF_size(style->widgetlabel.uifont_id, style->widgetlabel.points, U.dpi);
1173         ver_width = (int)BLF_width(style->widgetlabel.uifont_id, version_str) + 5;
1174         rev_width = (int)BLF_width(style->widgetlabel.uifont_id, revision_str) + 5;
1175 #endif //NAN_BUILDINFO
1176
1177         block= uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
1178         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN);
1179         
1180         but= uiDefBut(block, BUT_IMAGE, 0, "", 0, 10, 501, 282, NULL, 0.0, 0.0, 0, 0, "");
1181         uiButSetFunc(but, wm_block_splash_close, block, NULL);
1182         uiBlockSetFunc(block, wm_block_splash_refreshmenu, block, NULL);
1183         
1184 #ifdef NAN_BUILDINFO    
1185         uiDefBut(block, LABEL, 0, version_str, 494-ver_width, 282-24, ver_width, 20, NULL, 0, 0, 0, 0, NULL);
1186         uiDefBut(block, LABEL, 0, revision_str, 494-rev_width, 282-36, rev_width, 20, NULL, 0, 0, 0, 0, NULL);
1187 #endif //NAN_BUILDINFO
1188         
1189         layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 10, 2, 480, 110, style);
1190         
1191         uiBlockSetEmboss(block, UI_EMBOSS);
1192         /* show the splash menu (containing interaction presets), using python */
1193         if (mt) {
1194                 Menu menu= {NULL};
1195                 menu.layout= layout;
1196                 menu.type= mt;
1197                 mt->draw(C, &menu);
1198
1199 //              wmWindowManager *wm= CTX_wm_manager(C);
1200 //              uiItemM(layout, C, "USERPREF_MT_keyconfigs", U.keyconfigstr, ICON_NONE);
1201         }
1202         
1203         uiBlockSetEmboss(block, UI_EMBOSSP);
1204         uiLayoutSetOperatorContext(layout, WM_OP_EXEC_REGION_WIN);
1205         
1206         split = uiLayoutSplit(layout, 0, 0);
1207         col = uiLayoutColumn(split, 0);
1208         uiItemL(col, "Links", ICON_NONE);
1209         uiItemStringO(col, "Donations", ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/blenderorg/blender-foundation/donation-payment/");
1210         uiItemStringO(col, "Release Log", ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/development/release-logs/blender-257/");
1211         uiItemStringO(col, "Manual", ICON_URL, "WM_OT_url_open", "url", "http://wiki.blender.org/index.php/Doc:2.5/Manual");
1212         uiItemStringO(col, "Blender Website", ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/");
1213         uiItemStringO(col, "User Community", ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/community/user-community/"); // 
1214         if(strcmp(STRINGIFY(BLENDER_VERSION_CYCLE), "release")==0) {
1215                 BLI_snprintf(url, sizeof(url), "http://www.blender.org/documentation/blender_python_api_%d_%d" STRINGIFY(BLENDER_VERSION_CHAR) "_release", BLENDER_VERSION/100, BLENDER_VERSION%100);
1216         }
1217         else {
1218                 BLI_snprintf(url, sizeof(url), "http://www.blender.org/documentation/blender_python_api_%d_%d_%d", BLENDER_VERSION/100, BLENDER_VERSION%100, BLENDER_SUBVERSION);
1219         }
1220         uiItemStringO(col, "Python API Reference", ICON_URL, "WM_OT_url_open", "url", url);
1221         uiItemL(col, "", ICON_NONE);
1222
1223         col = uiLayoutColumn(split, 0);
1224
1225         if(wm_resource_check_prev()) {
1226                 uiItemO(col, NULL, ICON_NEW, "WM_OT_copy_prev_settings");
1227                 uiItemS(col);
1228         }
1229
1230         uiItemL(col, "Recent", ICON_NONE);
1231         for(recent = G.recent_files.first, i=0; (i<5) && (recent); recent = recent->next, i++) {
1232                 uiItemStringO(col, BLI_path_basename(recent->filepath), ICON_FILE_BLEND, "WM_OT_open_mainfile", "filepath", recent->filepath);
1233         }
1234
1235         uiItemS(col);
1236         uiItemO(col, NULL, ICON_RECOVER_LAST, "WM_OT_recover_last_session");
1237         uiItemL(col, "", ICON_NONE);
1238         
1239         uiCenteredBoundsBlock(block, 0);
1240         uiEndBlock(C, block);
1241         
1242         return block;
1243 }
1244
1245 static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
1246 {
1247         uiPupBlock(C, wm_block_create_splash, NULL);
1248         
1249         return OPERATOR_FINISHED;
1250 }
1251
1252 static void WM_OT_splash(wmOperatorType *ot)
1253 {
1254         ot->name= "Splash Screen";
1255         ot->idname= "WM_OT_splash";
1256         ot->description= "Opens a blocking popup region with release info";
1257         
1258         ot->invoke= wm_splash_invoke;
1259         ot->poll= WM_operator_winactive;
1260 }
1261
1262
1263 /* ***************** Search menu ************************* */
1264 static void operator_call_cb(struct bContext *C, void *UNUSED(arg1), void *arg2)
1265 {
1266         wmOperatorType *ot= arg2;
1267         
1268         if(ot)
1269                 WM_operator_name_call(C, ot->idname, WM_OP_INVOKE_DEFAULT, NULL);
1270 }
1271
1272 static void operator_search_cb(const struct bContext *C, void *UNUSED(arg), const char *str, uiSearchItems *items)
1273 {
1274         wmOperatorType *ot = WM_operatortype_first();
1275         
1276         for(; ot; ot= ot->next) {
1277                 
1278                 if(BLI_strcasestr(ot->name, str)) {
1279                         if(WM_operator_poll((bContext*)C, ot)) {
1280                                 char name[256];
1281                                 int len= strlen(ot->name);
1282                                 
1283                                 /* display name for menu, can hold hotkey */
1284                                 BLI_strncpy(name, ot->name, 256);
1285                                 
1286                                 /* check for hotkey */
1287                                 if(len < 256-6) {
1288                                         if(WM_key_event_operator_string(C, ot->idname, WM_OP_EXEC_DEFAULT, NULL, &name[len+1], 256-len-1))
1289                                                 name[len]= '|';
1290                                 }
1291                                 
1292                                 if(0==uiSearchItemAdd(items, name, ot, 0))
1293                                         break;
1294                         }
1295                 }
1296         }
1297 }
1298
1299 static uiBlock *wm_block_search_menu(bContext *C, ARegion *ar, void *UNUSED(arg_op))
1300 {
1301         static char search[256]= "";
1302         wmEvent event;
1303         wmWindow *win= CTX_wm_window(C);
1304         uiBlock *block;
1305         uiBut *but;
1306         
1307         block= uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
1308         uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
1309         
1310         but= uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, 256, 10, 10, 180, 19, 0, 0, "");
1311         uiButSetSearchFunc(but, operator_search_cb, NULL, operator_call_cb, NULL);
1312         
1313         /* fake button, it holds space for search items */
1314         uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxhHeight(), 180, uiSearchBoxhHeight(), NULL, 0, 0, 0, 0, NULL);
1315         
1316         uiPopupBoundsBlock(block, 6, 0, -20); /* move it downwards, mouse over button */
1317         uiEndBlock(C, block);
1318         
1319         event= *(win->eventstate);      /* XXX huh huh? make api call */
1320         event.type= EVT_BUT_OPEN;
1321         event.val= KM_PRESS;
1322         event.customdata= but;
1323         event.customdatafree= FALSE;
1324         wm_event_add(win, &event);
1325         
1326         return block;
1327 }
1328
1329 static int wm_search_menu_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
1330 {
1331         return OPERATOR_FINISHED;       
1332 }
1333
1334 static int wm_search_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1335 {
1336         uiPupBlock(C, wm_block_search_menu, op);
1337         
1338         return OPERATOR_CANCELLED;
1339 }
1340
1341 /* op->poll */
1342 static int wm_search_menu_poll(bContext *C)
1343 {
1344         if(CTX_wm_window(C)==NULL) {
1345                 return 0;
1346         }
1347         else {
1348                 ScrArea *sa= CTX_wm_area(C);
1349                 if(sa) {
1350                         if(sa->spacetype==SPACE_CONSOLE) return 0;  // XXX - so we can use the shortcut in the console
1351                         if(sa->spacetype==SPACE_TEXT) return 0;  // XXX - so we can use the spacebar in the text editor                 
1352                 }
1353                 else {
1354                         Object *editob= CTX_data_edit_object(C);
1355                         if(editob && editob->type==OB_FONT) return 0; // XXX - so we can use the spacebar for entering text
1356                 }
1357         }
1358         return 1;
1359 }
1360
1361 static void WM_OT_search_menu(wmOperatorType *ot)
1362 {
1363         ot->name= "Search Menu";
1364         ot->idname= "WM_OT_search_menu";
1365         
1366         ot->invoke= wm_search_menu_invoke;
1367         ot->exec= wm_search_menu_exec;
1368         ot->poll= wm_search_menu_poll;
1369 }
1370
1371 static int wm_call_menu_exec(bContext *C, wmOperator *op)
1372 {
1373         char idname[BKE_ST_MAXNAME];
1374         RNA_string_get(op->ptr, "name", idname);
1375
1376         uiPupMenuInvoke(C, idname);
1377
1378         return OPERATOR_CANCELLED;
1379 }
1380
1381 static void WM_OT_call_menu(wmOperatorType *ot)
1382 {
1383         ot->name= "Call Menu";
1384         ot->idname= "WM_OT_call_menu";
1385
1386         ot->exec= wm_call_menu_exec;
1387         ot->poll= WM_operator_winactive;
1388
1389         RNA_def_string(ot->srna, "name", "", BKE_ST_MAXNAME, "Name", "Name of the menu");
1390 }
1391
1392 /* ************ window / screen operator definitions ************** */
1393
1394 /* this poll functions is needed in place of WM_operator_winactive
1395  * while it crashes on full screen */
1396 static int wm_operator_winactive_normal(bContext *C)
1397 {
1398         wmWindow *win= CTX_wm_window(C);
1399
1400         if(win==NULL || win->screen==NULL || win->screen->full != SCREENNORMAL)
1401                 return 0;
1402
1403         return 1;
1404 }
1405
1406 static void WM_OT_window_duplicate(wmOperatorType *ot)
1407 {
1408         ot->name= "Duplicate Window";
1409         ot->idname= "WM_OT_window_duplicate";
1410         ot->description="Duplicate the current Blender window";
1411                 
1412         ot->exec= wm_window_duplicate_exec;
1413         ot->poll= wm_operator_winactive_normal;
1414 }
1415
1416 static void WM_OT_save_homefile(wmOperatorType *ot)
1417 {
1418         ot->name= "Save User Settings";
1419         ot->idname= "WM_OT_save_homefile";
1420         ot->description="Make the current file the default .blend file";
1421                 
1422         ot->invoke= WM_operator_confirm;
1423         ot->exec= WM_write_homefile;
1424         ot->poll= WM_operator_winactive;
1425 }
1426
1427 static void WM_OT_read_homefile(wmOperatorType *ot)
1428 {
1429         ot->name= "Reload Start-Up File";
1430         ot->idname= "WM_OT_read_homefile";
1431         ot->description="Open the default file (doesn't save the current file)";
1432         
1433         ot->invoke= WM_operator_confirm;
1434         ot->exec= WM_read_homefile_exec;
1435         /* ommit poll to run in background mode */
1436 }
1437
1438 static void WM_OT_read_factory_settings(wmOperatorType *ot)
1439 {
1440         ot->name= "Load Factory Settings";
1441         ot->idname= "WM_OT_read_factory_settings";
1442         ot->description="Load default file and user preferences";
1443         
1444         ot->invoke= WM_operator_confirm;
1445         ot->exec= WM_read_homefile_exec;
1446         /* ommit poll to run in background mode */
1447 }
1448
1449 /* *************** open file **************** */
1450
1451 static void open_set_load_ui(wmOperator *op)
1452 {
1453         if(!RNA_property_is_set(op->ptr, "load_ui"))
1454                 RNA_boolean_set(op->ptr, "load_ui", !(U.flag & USER_FILENOUI));
1455 }
1456
1457 static void open_set_use_scripts(wmOperator *op)
1458 {
1459         if(!RNA_property_is_set(op->ptr, "use_scripts")) {
1460                 /* use G_SCRIPT_AUTOEXEC rather then the userpref because this means if
1461                  * the flag has been disabled from the command line, then opening
1462                  * from the menu wont enable this setting. */
1463                 RNA_boolean_set(op->ptr, "use_scripts", (G.f & G_SCRIPT_AUTOEXEC));
1464         }
1465 }
1466
1467 static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1468 {
1469         const char *openname= G.main->name;
1470
1471         /* if possible, get the name of the most recently used .blend file */
1472         if (G.recent_files.first) {
1473                 struct RecentFile *recent = G.recent_files.first;
1474                 openname = recent->filepath;
1475         }
1476
1477         RNA_string_set(op->ptr, "filepath", openname);
1478         open_set_load_ui(op);
1479         open_set_use_scripts(op);
1480
1481         WM_event_add_fileselect(C, op);
1482
1483         return OPERATOR_RUNNING_MODAL;
1484 }
1485
1486 static int wm_open_mainfile_exec(bContext *C, wmOperator *op)
1487 {
1488         char path[FILE_MAX];
1489
1490         RNA_string_get(op->ptr, "filepath", path);
1491         open_set_load_ui(op);
1492         open_set_use_scripts(op);
1493
1494         if(RNA_boolean_get(op->ptr, "load_ui"))
1495                 G.fileflags &= ~G_FILE_NO_UI;
1496         else
1497                 G.fileflags |= G_FILE_NO_UI;
1498                 
1499         if(RNA_boolean_get(op->ptr, "use_scripts"))
1500                 G.f |= G_SCRIPT_AUTOEXEC;
1501         else
1502                 G.f &= ~G_SCRIPT_AUTOEXEC;
1503         
1504         // XXX wm in context is not set correctly after WM_read_file -> crash
1505         // do it before for now, but is this correct with multiple windows?
1506         WM_event_add_notifier(C, NC_WINDOW, NULL);
1507
1508         WM_read_file(C, path, op->reports);
1509         
1510         return OPERATOR_FINISHED;
1511 }
1512
1513 static void WM_OT_open_mainfile(wmOperatorType *ot)
1514 {
1515         ot->name= "Open Blender File";
1516         ot->idname= "WM_OT_open_mainfile";
1517         ot->description="Open a Blender file";
1518         
1519         ot->invoke= wm_open_mainfile_invoke;
1520         ot->exec= wm_open_mainfile_exec;
1521         ot->poll= WM_operator_winactive;
1522         
1523         WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH);
1524
1525         RNA_def_boolean(ot->srna, "load_ui", 1, "Load UI", "Load user interface setup in the .blend file");
1526         RNA_def_boolean(ot->srna, "use_scripts", 1, "Trusted Source", "Allow blend file execute scripts automatically, default available from system preferences");
1527 }
1528
1529 /* **************** link/append *************** */
1530
1531 static int wm_link_append_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1532 {
1533         if(!RNA_property_is_set(op->ptr, "relative_path"))
1534                 RNA_boolean_set(op->ptr, "relative_path", U.flag & USER_RELPATHS);
1535
1536         if(RNA_property_is_set(op->ptr, "filepath")) {
1537                 return WM_operator_call(C, op);
1538         } 
1539         else {
1540                 /* XXX TODO solve where to get last linked library from */
1541                 RNA_string_set(op->ptr, "filepath", G.lib);
1542                 WM_event_add_fileselect(C, op);
1543                 return OPERATOR_RUNNING_MODAL;
1544         }
1545 }
1546
1547 static short wm_link_append_flag(wmOperator *op)
1548 {
1549         short flag= 0;
1550
1551         if(RNA_boolean_get(op->ptr, "autoselect")) flag |= FILE_AUTOSELECT;
1552         if(RNA_boolean_get(op->ptr, "active_layer")) flag |= FILE_ACTIVELAY;
1553         if(RNA_boolean_get(op->ptr, "relative_path")) flag |= FILE_RELPATH;
1554         if(RNA_boolean_get(op->ptr, "link")) flag |= FILE_LINK;
1555         if(RNA_boolean_get(op->ptr, "instance_groups")) flag |= FILE_GROUP_INSTANCE;
1556
1557         return flag;
1558 }
1559
1560 static int wm_link_append_exec(bContext *C, wmOperator *op)
1561 {
1562         Main *bmain= CTX_data_main(C);
1563         Scene *scene= CTX_data_scene(C);
1564         Main *mainl= NULL;
1565         BlendHandle *bh;
1566         PropertyRNA *prop;
1567         char name[FILE_MAX], dir[FILE_MAX], libname[FILE_MAX], group[GROUP_MAX];
1568         int idcode, totfiles=0;
1569         short flag;
1570
1571         name[0] = '\0';
1572         RNA_string_get(op->ptr, "filename", name);
1573         RNA_string_get(op->ptr, "directory", dir);
1574
1575         /* test if we have a valid data */
1576         if(BLO_is_a_library(dir, libname, group) == 0) {
1577                 BKE_report(op->reports, RPT_ERROR, "Not a library");
1578                 return OPERATOR_CANCELLED;
1579         }
1580         else if(group[0] == 0) {
1581                 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1582                 return OPERATOR_CANCELLED;
1583         }
1584         else if(BLI_path_cmp(bmain->name, libname) == 0) {
1585                 BKE_report(op->reports, RPT_ERROR, "Cannot use current file as library");
1586                 return OPERATOR_CANCELLED;
1587         }
1588
1589         /* check if something is indicated for append/link */
1590         prop = RNA_struct_find_property(op->ptr, "files");
1591         if(prop) {
1592                 totfiles= RNA_property_collection_length(op->ptr, prop);
1593                 if(totfiles == 0) {
1594                         if(name[0] == '\0') {
1595                                 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1596                                 return OPERATOR_CANCELLED;
1597                         }
1598                 }
1599         }
1600         else if(name[0] == '\0') {
1601                 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1602                 return OPERATOR_CANCELLED;
1603         }
1604
1605         bh = BLO_blendhandle_from_file(libname, op->reports);
1606
1607         if(bh == NULL) {
1608                 /* unlikely since we just browsed it, but possible
1609                  * error reports will have been made by BLO_blendhandle_from_file() */
1610                 return OPERATOR_CANCELLED;
1611         }
1612
1613
1614         /* from here down, no error returns */
1615
1616         idcode = BKE_idcode_from_name(group);
1617
1618         /* now we have or selected, or an indicated file */
1619         if(RNA_boolean_get(op->ptr, "autoselect"))
1620                 scene_deselect_all(scene);
1621
1622         
1623         flag = wm_link_append_flag(op);
1624
1625         /* sanity checks for flag */
1626         if(scene->id.lib && (flag & FILE_GROUP_INSTANCE)) {
1627                 /* TODO, user never gets this message */
1628                 BKE_reportf(op->reports, RPT_WARNING, "Scene '%s' is linked, group instance disabled", scene->id.name+2);
1629                 flag &= ~FILE_GROUP_INSTANCE;
1630         }
1631
1632
1633         /* tag everything, all untagged data can be made local
1634          * its also generally useful to know what is new
1635          *
1636          * take extra care flag_all_listbases_ids(LIB_LINK_TAG, 0) is called after! */
1637         flag_all_listbases_ids(LIB_PRE_EXISTING, 1);
1638
1639         /* here appending/linking starts */
1640         mainl = BLO_library_append_begin(C, &bh, libname);
1641         if(totfiles == 0) {
1642                 BLO_library_append_named_part(C, mainl, &bh, name, idcode, flag);
1643         }
1644         else {
1645                 RNA_BEGIN(op->ptr, itemptr, "files") {
1646                         RNA_string_get(&itemptr, "name", name);
1647                         BLO_library_append_named_part(C, mainl, &bh, name, idcode, flag);
1648                 }
1649                 RNA_END;
1650         }
1651         BLO_library_append_end(C, mainl, &bh, idcode, flag);
1652         
1653         /* mark all library linked objects to be updated */
1654         recalc_all_library_objects(bmain);
1655
1656         /* append, rather than linking */
1657         if((flag & FILE_LINK)==0) {
1658                 Library *lib= BLI_findstring(&bmain->library, libname, offsetof(Library, filepath));
1659                 if(lib) all_local(lib, 1);
1660                 else    BLI_assert(!"cant find name of just added library!");
1661         }
1662
1663         /* important we unset, otherwise these object wont
1664          * link into other scenes from this blend file */
1665         flag_all_listbases_ids(LIB_PRE_EXISTING, 0);
1666
1667         /* recreate dependency graph to include new objects */
1668         DAG_scene_sort(bmain, scene);
1669         DAG_ids_flush_update(bmain, 0);
1670
1671         BLO_blendhandle_close(bh);
1672
1673         /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
1674         BLI_strncpy(G.lib, dir, FILE_MAX);
1675
1676         WM_event_add_notifier(C, NC_WINDOW, NULL);
1677
1678         return OPERATOR_FINISHED;
1679 }
1680
1681 static void WM_OT_link_append(wmOperatorType *ot)
1682 {
1683         ot->name= "Link/Append from Library";
1684         ot->idname= "WM_OT_link_append";
1685         ot->description= "Link or Append from a Library .blend file";
1686         
1687         ot->invoke= wm_link_append_invoke;
1688         ot->exec= wm_link_append_exec;
1689         ot->poll= WM_operator_winactive;
1690         
1691         ot->flag |= OPTYPE_UNDO;
1692
1693         WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_LOADLIB, FILE_OPENFILE, WM_FILESEL_FILEPATH|WM_FILESEL_DIRECTORY|WM_FILESEL_FILENAME| WM_FILESEL_RELPATH);
1694         
1695         RNA_def_boolean(ot->srna, "link", 1, "Link", "Link the objects or datablocks rather than appending");
1696         RNA_def_boolean(ot->srna, "autoselect", 1, "Select", "Select the linked objects");
1697         RNA_def_boolean(ot->srna, "active_layer", 1, "Active Layer", "Put the linked objects on the active layer");
1698         RNA_def_boolean(ot->srna, "instance_groups", 1, "Instance Groups", "Create instances for each group as a DupliGroup");
1699
1700         RNA_def_collection_runtime(ot->srna, "files", &RNA_OperatorFileListElement, "Files", "");
1701 }       
1702
1703 /* *************** recover last session **************** */
1704
1705 static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
1706 {
1707         char filename[FILE_MAX];
1708
1709         G.fileflags |= G_FILE_RECOVER;
1710
1711         // XXX wm in context is not set correctly after WM_read_file -> crash
1712         // do it before for now, but is this correct with multiple windows?
1713         WM_event_add_notifier(C, NC_WINDOW, NULL);
1714
1715         /* load file */
1716         BLI_make_file_string("/", filename, btempdir, "quit.blend");
1717         WM_read_file(C, filename, op->reports);
1718
1719         G.fileflags &= ~G_FILE_RECOVER;
1720         return OPERATOR_FINISHED;
1721 }
1722
1723 static void WM_OT_recover_last_session(wmOperatorType *ot)
1724 {
1725         ot->name= "Recover Last Session";
1726         ot->idname= "WM_OT_recover_last_session";
1727         ot->description="Open the last closed file (\"quit.blend\")";
1728         
1729         ot->exec= wm_recover_last_session_exec;
1730         ot->poll= WM_operator_winactive;
1731 }
1732
1733 /* *************** recover auto save **************** */
1734
1735 static int wm_recover_auto_save_exec(bContext *C, wmOperator *op)
1736 {
1737         char path[FILE_MAX];
1738
1739         RNA_string_get(op->ptr, "filepath", path);
1740
1741         G.fileflags |= G_FILE_RECOVER;
1742
1743         // XXX wm in context is not set correctly after WM_read_file -> crash
1744         // do it before for now, but is this correct with multiple windows?
1745         WM_event_add_notifier(C, NC_WINDOW, NULL);
1746
1747         /* load file */
1748         WM_read_file(C, path, op->reports);
1749
1750         G.fileflags &= ~G_FILE_RECOVER;
1751
1752         return OPERATOR_FINISHED;
1753 }
1754
1755 static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1756 {
1757         char filename[FILE_MAX];
1758
1759         wm_autosave_location(filename);
1760         RNA_string_set(op->ptr, "filepath", filename);
1761         WM_event_add_fileselect(C, op);
1762
1763         return OPERATOR_RUNNING_MODAL;
1764 }
1765
1766 static void WM_OT_recover_auto_save(wmOperatorType *ot)
1767 {
1768         ot->name= "Recover Auto Save";
1769         ot->idname= "WM_OT_recover_auto_save";
1770         ot->description="Open an automatically saved file to recover it";
1771         
1772         ot->exec= wm_recover_auto_save_exec;
1773         ot->invoke= wm_recover_auto_save_invoke;
1774         ot->poll= WM_operator_winactive;
1775
1776         WM_operator_properties_filesel(ot, BLENDERFILE, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH);
1777 }
1778
1779 /* *************** save file as **************** */
1780
1781 static void untitled(char *name)
1782 {
1783         if(G.save_over == 0 && strlen(name) < FILE_MAX-16) {
1784                 char *c= BLI_last_slash(name);
1785                 
1786                 if(c)
1787                         strcpy(&c[1], "untitled.blend");
1788                 else
1789                         strcpy(name, "untitled.blend");
1790         }
1791 }
1792
1793 static void save_set_compress(wmOperator *op)
1794 {
1795         if(!RNA_property_is_set(op->ptr, "compress")) {
1796                 if(G.save_over) /* keep flag for existing file */
1797                         RNA_boolean_set(op->ptr, "compress", G.fileflags & G_FILE_COMPRESS);
1798                 else /* use userdef for new file */
1799                         RNA_boolean_set(op->ptr, "compress", U.flag & USER_FILECOMPRESS);
1800         }
1801 }
1802
1803 static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1804 {
1805         char name[FILE_MAX];
1806
1807         save_set_compress(op);
1808         
1809         /* if not saved before, get the name of the most recently used .blend file */
1810         if(G.main->name[0]==0 && G.recent_files.first) {
1811                 struct RecentFile *recent = G.recent_files.first;
1812                 BLI_strncpy(name, recent->filepath, FILE_MAX);
1813         }
1814         else
1815                 BLI_strncpy(name, G.main->name, FILE_MAX);
1816         
1817         untitled(name);
1818         RNA_string_set(op->ptr, "filepath", name);
1819         
1820         WM_event_add_fileselect(C, op);
1821
1822         return OPERATOR_RUNNING_MODAL;
1823 }
1824
1825 /* function used for WM_OT_save_mainfile too */
1826 static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
1827 {
1828         char path[FILE_MAX];
1829         int fileflags;
1830         int copy=0;
1831
1832         save_set_compress(op);
1833         
1834         if(RNA_property_is_set(op->ptr, "filepath"))
1835                 RNA_string_get(op->ptr, "filepath", path);
1836         else {
1837                 BLI_strncpy(path, G.main->name, FILE_MAX);
1838                 untitled(path);
1839         }
1840
1841         if(RNA_property_is_set(op->ptr, "copy"))
1842                 copy = RNA_boolean_get(op->ptr, "copy");
1843         
1844         fileflags= G.fileflags;
1845
1846         /* set compression flag */
1847         if(RNA_boolean_get(op->ptr, "compress"))                fileflags |=  G_FILE_COMPRESS;
1848         else                                                                                    fileflags &= ~G_FILE_COMPRESS;
1849         if(RNA_boolean_get(op->ptr, "relative_remap"))  fileflags |=  G_FILE_RELATIVE_REMAP;
1850         else                                                                                    fileflags &= ~G_FILE_RELATIVE_REMAP;
1851
1852         if ( WM_write_file(C, path, fileflags, op->reports, copy) != 0)
1853                 return OPERATOR_CANCELLED;
1854
1855         WM_event_add_notifier(C, NC_WM|ND_FILESAVE, NULL);
1856
1857         return OPERATOR_FINISHED;
1858 }
1859
1860 /* function used for WM_OT_save_mainfile too */
1861 static int blend_save_check(bContext *UNUSED(C), wmOperator *op)
1862 {
1863         char filepath[FILE_MAX];
1864         RNA_string_get(op->ptr, "filepath", filepath);
1865         if(BLI_replace_extension(filepath, sizeof(filepath), ".blend")) {
1866                 RNA_string_set(op->ptr, "filepath", filepath);
1867                 return TRUE;
1868         }
1869         return FALSE;
1870 }
1871
1872 static void WM_OT_save_as_mainfile(wmOperatorType *ot)
1873 {
1874         ot->name= "Save As Blender File";
1875         ot->idname= "WM_OT_save_as_mainfile";
1876         ot->description="Save the current file in the desired location";
1877         
1878         ot->invoke= wm_save_as_mainfile_invoke;
1879         ot->exec= wm_save_as_mainfile_exec;
1880         ot->check= blend_save_check;
1881         /* ommit window poll so this can work in background mode */
1882
1883         WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH);
1884         RNA_def_boolean(ot->srna, "compress", 0, "Compress", "Write compressed .blend file");
1885         RNA_def_boolean(ot->srna, "relative_remap", 1, "Remap Relative", "Remap relative paths when saving in a different directory");
1886         RNA_def_boolean(ot->srna, "copy", 0, "Save Copy", "Save a copy of the actual working state but does not make saved file active.");
1887 }
1888
1889 /* *************** save file directly ******** */
1890
1891 static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1892 {
1893         char name[FILE_MAX];
1894         int check_existing=1;
1895         
1896         /* cancel if no active window */
1897         if (CTX_wm_window(C) == NULL)
1898                 return OPERATOR_CANCELLED;
1899
1900         save_set_compress(op);
1901
1902         /* if not saved before, get the name of the most recently used .blend file */
1903         if(G.main->name[0]==0 && G.recent_files.first) {
1904                 struct RecentFile *recent = G.recent_files.first;
1905                 BLI_strncpy(name, recent->filepath, FILE_MAX);
1906         }
1907         else
1908                 BLI_strncpy(name, G.main->name, FILE_MAX);
1909
1910         untitled(name);
1911         
1912         RNA_string_set(op->ptr, "filepath", name);
1913         
1914         if (RNA_struct_find_property(op->ptr, "check_existing"))
1915                 if (RNA_boolean_get(op->ptr, "check_existing")==0)
1916                         check_existing = 0;
1917         
1918         if (G.save_over) {
1919                 if (check_existing)
1920                         uiPupMenuSaveOver(C, op, name);
1921                 else {
1922                         wm_save_as_mainfile_exec(C, op);
1923                 }
1924         } else {
1925                 WM_event_add_fileselect(C, op);
1926         }
1927         
1928         return OPERATOR_RUNNING_MODAL;
1929 }
1930
1931 static void WM_OT_save_mainfile(wmOperatorType *ot)
1932 {
1933         ot->name= "Save Blender File";
1934         ot->idname= "WM_OT_save_mainfile";
1935         ot->description="Save the current Blender file";
1936         
1937         ot->invoke= wm_save_mainfile_invoke;
1938         ot->exec= wm_save_as_mainfile_exec;
1939         ot->check= blend_save_check;
1940         ot->poll= NULL;
1941         
1942         WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH);
1943         RNA_def_boolean(ot->srna, "compress", 0, "Compress", "Write compressed .blend file");
1944         RNA_def_boolean(ot->srna, "relative_remap", 0, "Remap Relative", "Remap relative paths when saving in a different directory");
1945 }
1946
1947 /* XXX: move these collada operators to a more appropriate place */
1948 #ifdef WITH_COLLADA
1949
1950 #include "../../collada/collada.h"
1951
1952 static int wm_collada_export_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1953 {       
1954         if(!RNA_property_is_set(op->ptr, "filepath")) {
1955                 char filepath[FILE_MAX];
1956                 BLI_strncpy(filepath, G.main->name, sizeof(filepath));
1957                 BLI_replace_extension(filepath, sizeof(filepath), ".dae");
1958                 RNA_string_set(op->ptr, "filepath", filepath);
1959         }
1960
1961         WM_event_add_fileselect(C, op);
1962
1963         return OPERATOR_RUNNING_MODAL;
1964 }
1965
1966 /* function used for WM_OT_save_mainfile too */
1967 static int wm_collada_export_exec(bContext *C, wmOperator *op)
1968 {
1969         char filename[FILE_MAX];
1970         
1971         if(!RNA_property_is_set(op->ptr, "filepath")) {
1972                 BKE_report(op->reports, RPT_ERROR, "No filename given");
1973                 return OPERATOR_CANCELLED;
1974         }
1975
1976         RNA_string_get(op->ptr, "filepath", filename);
1977         if(collada_export(CTX_data_scene(C), filename)) {
1978                 return OPERATOR_FINISHED;
1979         }
1980         else {
1981                 return OPERATOR_CANCELLED;
1982         }
1983 }
1984
1985 static void WM_OT_collada_export(wmOperatorType *ot)
1986 {
1987         ot->name= "Export COLLADA";
1988         ot->idname= "WM_OT_collada_export";
1989         
1990         ot->invoke= wm_collada_export_invoke;
1991         ot->exec= wm_collada_export_exec;
1992         ot->poll= WM_operator_winactive;
1993         
1994         WM_operator_properties_filesel(ot, FOLDERFILE|COLLADAFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH);
1995 }
1996
1997 /* function used for WM_OT_save_mainfile too */
1998 static int wm_collada_import_exec(bContext *C, wmOperator *op)
1999 {
2000         char filename[FILE_MAX];
2001         
2002         if(!RNA_property_is_set(op->ptr, "filepath")) {
2003                 BKE_report(op->reports, RPT_ERROR, "No filename given");
2004                 return OPERATOR_CANCELLED;
2005         }
2006
2007         RNA_string_get(op->ptr, "filepath", filename);
2008         collada_import(C, filename);
2009         
2010         return OPERATOR_FINISHED;
2011 }
2012
2013 static void WM_OT_collada_import(wmOperatorType *ot)
2014 {
2015         ot->name= "Import COLLADA";
2016         ot->idname= "WM_OT_collada_import";
2017         
2018         ot->invoke= WM_operator_filesel;
2019         ot->exec= wm_collada_import_exec;
2020         ot->poll= WM_operator_winactive;
2021         
2022         WM_operator_properties_filesel(ot, FOLDERFILE|COLLADAFILE, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH);
2023 }
2024
2025 #endif
2026
2027
2028 /* *********************** */
2029
2030 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
2031 {
2032         ot->name= "Toggle Fullscreen";
2033         ot->idname= "WM_OT_window_fullscreen_toggle";
2034         ot->description="Toggle the current window fullscreen";
2035
2036         ot->exec= wm_window_fullscreen_toggle_exec;
2037         ot->poll= WM_operator_winactive;
2038 }
2039
2040 static int wm_exit_blender_op(bContext *C, wmOperator *op)
2041 {
2042         WM_operator_free(op);
2043         
2044         WM_exit(C);     
2045         
2046         return OPERATOR_FINISHED;
2047 }
2048
2049 static void WM_OT_quit_blender(wmOperatorType *ot)
2050 {
2051         ot->name= "Quit Blender";
2052         ot->idname= "WM_OT_quit_blender";
2053         ot->description= "Quit Blender";
2054
2055         ot->invoke= WM_operator_confirm;
2056         ot->exec= wm_exit_blender_op;
2057         ot->poll= WM_operator_winactive;
2058 }
2059
2060 /* *********************** */
2061
2062 #if defined(WIN32)
2063
2064 static int wm_console_toggle_op(bContext *UNUSED(C), wmOperator *UNUSED(op))
2065 {
2066         GHOST_toggleConsole(2);
2067         return OPERATOR_FINISHED;
2068 }
2069
2070 static void WM_OT_console_toggle(wmOperatorType *ot)
2071 {
2072         ot->name= "Toggle System Console";
2073         ot->idname= "WM_OT_console_toggle";
2074         ot->description= "Toggle System Console";
2075         
2076         ot->exec= wm_console_toggle_op;
2077         ot->poll= WM_operator_winactive;
2078 }
2079
2080 #endif
2081
2082 /* ************ default paint cursors, draw always around cursor *********** */
2083 /*
2084  - returns handler to free 
2085  - poll(bContext): returns 1 if draw should happen
2086  - draw(bContext): drawing callback for paint cursor
2087 */
2088
2089 void *WM_paint_cursor_activate(wmWindowManager *wm, int (*poll)(bContext *C),
2090                                    wmPaintCursorDraw draw, void *customdata)
2091 {
2092         wmPaintCursor *pc= MEM_callocN(sizeof(wmPaintCursor), "paint cursor");
2093         
2094         BLI_addtail(&wm->paintcursors, pc);
2095         
2096         pc->customdata = customdata;
2097         pc->poll= poll;
2098         pc->draw= draw;
2099         
2100         return pc;
2101 }
2102
2103 void WM_paint_cursor_end(wmWindowManager *wm, void *handle)
2104 {
2105         wmPaintCursor *pc;
2106         
2107         for(pc= wm->paintcursors.first; pc; pc= pc->next) {
2108                 if(pc == (wmPaintCursor *)handle) {
2109                         BLI_remlink(&wm->paintcursors, pc);
2110                         MEM_freeN(pc);
2111                         return;
2112                 }
2113         }
2114 }
2115
2116 /* ************ window gesture operator-callback definitions ************** */
2117 /*
2118  * These are default callbacks for use in operators requiring gesture input
2119  */
2120
2121 /* **************** Border gesture *************** */
2122
2123 /* Border gesture has two types:
2124    1) WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border 
2125    2) WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends
2126
2127    It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type)
2128 */
2129
2130 static int border_apply_rect(wmOperator *op)
2131 {
2132         wmGesture *gesture= op->customdata;
2133         rcti *rect= gesture->customdata;
2134         
2135         if(rect->xmin==rect->xmax || rect->ymin==rect->ymax)
2136                 return 0;
2137
2138         
2139         /* operator arguments and storage. */
2140         RNA_int_set(op->ptr, "xmin", MIN2(rect->xmin, rect->xmax) );
2141         RNA_int_set(op->ptr, "ymin", MIN2(rect->ymin, rect->ymax) );
2142         RNA_int_set(op->ptr, "xmax", MAX2(rect->xmin, rect->xmax) );
2143         RNA_int_set(op->ptr, "ymax", MAX2(rect->ymin, rect->ymax) );
2144
2145         return 1;
2146 }
2147
2148 static int border_apply(bContext *C, wmOperator *op, int gesture_mode)
2149 {
2150         if (!border_apply_rect(op))
2151                 return 0;
2152         
2153         /* XXX weak; border should be configured for this without reading event types */
2154         if( RNA_struct_find_property(op->ptr, "gesture_mode") )
2155                 RNA_int_set(op->ptr, "gesture_mode", gesture_mode);
2156
2157         op->type->exec(C, op);
2158         return 1;
2159 }
2160
2161 static void wm_gesture_end(bContext *C, wmOperator *op)
2162 {
2163         wmGesture *gesture= op->customdata;
2164         
2165         WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
2166         op->customdata= NULL;
2167
2168         ED_area_tag_redraw(CTX_wm_area(C));
2169         
2170         if( RNA_struct_find_property(op->ptr, "cursor") )
2171                 WM_cursor_restore(CTX_wm_window(C));
2172 }
2173
2174 int WM_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
2175 {
2176         if(ISTWEAK(event->type))
2177                 op->customdata= WM_gesture_new(C, event, WM_GESTURE_RECT);
2178         else
2179                 op->customdata= WM_gesture_new(C, event, WM_GESTURE_CROSS_RECT);
2180
2181         /* add modal handler */
2182         WM_event_add_modal_handler(C, op);
2183         
2184         wm_gesture_tag_redraw(C);
2185
2186         return OPERATOR_RUNNING_MODAL;
2187 }
2188
2189 int WM_border_select_modal(bContext *C, wmOperator *op, wmEvent *event)
2190 {
2191         wmGesture *gesture= op->customdata;
2192         rcti *rect= gesture->customdata;
2193         int sx, sy;
2194         
2195         if(event->type== MOUSEMOVE) {
2196                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2197
2198                 if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
2199                         rect->xmin= rect->xmax= event->x - sx;
2200                         rect->ymin= rect->ymax= event->y - sy;
2201                 }
2202                 else {
2203                         rect->xmax= event->x - sx;
2204                         rect->ymax= event->y - sy;
2205                 }
2206                 border_apply_rect(op);
2207
2208                 wm_gesture_tag_redraw(C);
2209         }
2210         else if (event->type==EVT_MODAL_MAP) {
2211                 switch (event->val) {
2212                 case GESTURE_MODAL_BEGIN:
2213                         if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
2214                                 gesture->mode= 1;
2215                                 wm_gesture_tag_redraw(C);
2216                         }
2217                         break;
2218                 case GESTURE_MODAL_SELECT:
2219                 case GESTURE_MODAL_DESELECT:
2220                 case GESTURE_MODAL_IN:
2221                 case GESTURE_MODAL_OUT:
2222                         if(border_apply(C, op, event->val)) {
2223                                 wm_gesture_end(C, op);
2224                                 return OPERATOR_FINISHED;
2225                         }
2226                         wm_gesture_end(C, op);
2227                         return OPERATOR_CANCELLED;
2228                         break;
2229
2230                 case GESTURE_MODAL_CANCEL:
2231                         wm_gesture_end(C, op);
2232                         return OPERATOR_CANCELLED;
2233                 }
2234
2235         }
2236 //      // Allow view navigation???
2237 //      else {
2238 //              return OPERATOR_PASS_THROUGH;
2239 //      }
2240
2241         return OPERATOR_RUNNING_MODAL;
2242 }
2243
2244 /* **************** circle gesture *************** */
2245 /* works now only for selection or modal paint stuff, calls exec while hold mouse, exit on release */
2246
2247 #ifdef GESTURE_MEMORY
2248 int circle_select_size= 25; // XXX - need some operator memory thing\!
2249 #endif
2250
2251 int WM_gesture_circle_invoke(bContext *C, wmOperator *op, wmEvent *event)
2252 {
2253         op->customdata= WM_gesture_new(C, event, WM_GESTURE_CIRCLE);
2254         
2255         /* add modal handler */
2256         WM_event_add_modal_handler(C, op);
2257         
2258         wm_gesture_tag_redraw(C);
2259         
2260         return OPERATOR_RUNNING_MODAL;
2261 }
2262
2263 static void gesture_circle_apply(bContext *C, wmOperator *op)
2264 {
2265         wmGesture *gesture= op->customdata;
2266         rcti *rect= gesture->customdata;
2267         
2268         if(RNA_int_get(op->ptr, "gesture_mode")==GESTURE_MODAL_NOP)
2269                 return;
2270
2271         /* operator arguments and storage. */
2272         RNA_int_set(op->ptr, "x", rect->xmin);
2273         RNA_int_set(op->ptr, "y", rect->ymin);
2274         RNA_int_set(op->ptr, "radius", rect->xmax);
2275         
2276         if(op->type->exec)
2277                 op->type->exec(C, op);
2278
2279 #ifdef GESTURE_MEMORY
2280         circle_select_size= rect->xmax;
2281 #endif
2282 }
2283
2284 int WM_gesture_circle_modal(bContext *C, wmOperator *op, wmEvent *event)
2285 {
2286         wmGesture *gesture= op->customdata;
2287         rcti *rect= gesture->customdata;
2288         int sx, sy;
2289
2290         if(event->type== MOUSEMOVE) {
2291                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2292
2293                 rect->xmin= event->x - sx;
2294                 rect->ymin= event->y - sy;
2295
2296                 wm_gesture_tag_redraw(C);
2297
2298                 if(gesture->mode)
2299                         gesture_circle_apply(C, op);
2300         }
2301         else if (event->type==EVT_MODAL_MAP) {
2302                 switch (event->val) {
2303                 case GESTURE_MODAL_CIRCLE_ADD:
2304                         rect->xmax += 2 + rect->xmax/10;
2305                         wm_gesture_tag_redraw(C);
2306                         break;
2307                 case GESTURE_MODAL_CIRCLE_SUB:
2308                         rect->xmax -= 2 + rect->xmax/10;
2309                         if(rect->xmax < 1) rect->xmax= 1;
2310                         wm_gesture_tag_redraw(C);
2311                         break;
2312                 case GESTURE_MODAL_SELECT:
2313                 case GESTURE_MODAL_DESELECT:
2314                 case GESTURE_MODAL_NOP:
2315                         if(RNA_struct_find_property(op->ptr, "gesture_mode"))
2316                                 RNA_int_set(op->ptr, "gesture_mode", event->val);
2317
2318                         if(event->val != GESTURE_MODAL_NOP) {
2319                                 /* apply first click */
2320                                 gesture_circle_apply(C, op);
2321                                 gesture->mode= 1;
2322                                 wm_gesture_tag_redraw(C);
2323                         }
2324                         break;
2325
2326                 case GESTURE_MODAL_CANCEL:
2327                 case GESTURE_MODAL_CONFIRM:
2328                         wm_gesture_end(C, op);
2329                         return OPERATOR_FINISHED; /* use finish or we dont get an undo */
2330                 }
2331         }
2332 //      // Allow view navigation???
2333 //      else {
2334 //              return OPERATOR_PASS_THROUGH;
2335 //      }
2336
2337         return OPERATOR_RUNNING_MODAL;
2338 }
2339
2340 #if 0
2341 /* template to copy from */
2342 void WM_OT_circle_gesture(wmOperatorType *ot)
2343 {
2344         ot->name= "Circle Gesture";
2345         ot->idname= "WM_OT_circle_gesture";
2346         ot->description="Enter rotate mode with a circular gesture";
2347         
2348         ot->invoke= WM_gesture_circle_invoke;
2349         ot->modal= WM_gesture_circle_modal;
2350         
2351         ot->poll= WM_operator_winactive;
2352         
2353         RNA_def_property(ot->srna, "x", PROP_INT, PROP_NONE);
2354         RNA_def_property(ot->srna, "y", PROP_INT, PROP_NONE);
2355         RNA_def_property(ot->srna, "radius", PROP_INT, PROP_NONE);
2356
2357 }
2358 #endif
2359
2360 /* **************** Tweak gesture *************** */
2361
2362 static void tweak_gesture_modal(bContext *C, wmEvent *event)
2363 {
2364         wmWindow *window= CTX_wm_window(C);
2365         wmGesture *gesture= window->tweak;
2366         rcti *rect= gesture->customdata;
2367         int sx, sy, val;
2368         
2369         switch(event->type) {
2370                 case MOUSEMOVE:
2371                 case INBETWEEN_MOUSEMOVE:
2372                         
2373                         wm_subwindow_getorigin(window, gesture->swinid, &sx, &sy);
2374                         
2375                         rect->xmax= event->x - sx;
2376                         rect->ymax= event->y - sy;
2377                         
2378                         if((val= wm_gesture_evaluate(gesture))) {
2379                                 wmEvent tevent;
2380
2381                                 tevent= *(window->eventstate);
2382                                 if(gesture->event_type==LEFTMOUSE)
2383                                         tevent.type= EVT_TWEAK_L;
2384                                 else if(gesture->event_type==RIGHTMOUSE)
2385                                         tevent.type= EVT_TWEAK_R;
2386                                 else
2387                                         tevent.type= EVT_TWEAK_M;
2388                                 tevent.val= val;
2389                                 /* mouse coords! */
2390                                 wm_event_add(window, &tevent);
2391                                 
2392                                 WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
2393                         }
2394                         
2395                         break;
2396                         
2397                 case LEFTMOUSE:
2398                 case RIGHTMOUSE:
2399                 case MIDDLEMOUSE:
2400                         if(gesture->event_type==event->type) {
2401                                 WM_gesture_end(C, gesture);
2402
2403                                 /* when tweak fails we should give the other keymap entries a chance */
2404                                 event->val= KM_RELEASE;
2405                         }
2406                         break;
2407                 default:
2408                         if(!ISTIMER(event->type)) {
2409                                 WM_gesture_end(C, gesture);
2410                         }
2411                         break;
2412         }
2413 }
2414
2415 /* standard tweak, called after window handlers passed on event */
2416 void wm_tweakevent_test(bContext *C, wmEvent *event, int action)
2417 {
2418         wmWindow *win= CTX_wm_window(C);
2419         
2420         if(win->tweak==NULL) {
2421                 if(CTX_wm_region(C)) {
2422                         if(event->val==KM_PRESS) { 
2423                                 if( ELEM3(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) )
2424                                         win->tweak= WM_gesture_new(C, event, WM_GESTURE_TWEAK);
2425                         }
2426                 }
2427         }
2428         else {
2429                 /* no tweaks if event was handled */
2430                 if((action & WM_HANDLER_BREAK)) {
2431                         WM_gesture_end(C, win->tweak);
2432                 }
2433                 else
2434                         tweak_gesture_modal(C, event);
2435         }
2436 }
2437
2438 /* *********************** lasso gesture ****************** */
2439
2440 int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, wmEvent *event)
2441 {
2442         op->customdata= WM_gesture_new(C, event, WM_GESTURE_LASSO);
2443         
2444         /* add modal handler */
2445         WM_event_add_modal_handler(C, op);
2446         
2447         wm_gesture_tag_redraw(C);
2448         
2449         if( RNA_struct_find_property(op->ptr, "cursor") )
2450                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2451         
2452         return OPERATOR_RUNNING_MODAL;
2453 }
2454
2455 int WM_gesture_lines_invoke(bContext *C, wmOperator *op, wmEvent *event)
2456 {
2457         op->customdata= WM_gesture_new(C, event, WM_GESTURE_LINES);
2458         
2459         /* add modal handler */
2460         WM_event_add_modal_handler(C, op);
2461         
2462         wm_gesture_tag_redraw(C);
2463         
2464         if( RNA_struct_find_property(op->ptr, "cursor") )
2465                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2466         
2467         return OPERATOR_RUNNING_MODAL;
2468 }
2469
2470
2471 static void gesture_lasso_apply(bContext *C, wmOperator *op)
2472 {
2473         wmGesture *gesture= op->customdata;
2474         PointerRNA itemptr;
2475         float loc[2];
2476         int i;
2477         short *lasso= gesture->customdata;
2478         
2479         /* operator storage as path. */
2480
2481         for(i=0; i<gesture->points; i++, lasso+=2) {
2482                 loc[0]= lasso[0];
2483                 loc[1]= lasso[1];
2484                 RNA_collection_add(op->ptr, "path", &itemptr);
2485                 RNA_float_set_array(&itemptr, "loc", loc);
2486         }
2487         
2488         wm_gesture_end(C, op);
2489                 
2490         if(op->type->exec)
2491                 op->type->exec(C, op);
2492         
2493 }
2494
2495 int WM_gesture_lasso_modal(bContext *C, wmOperator *op, wmEvent *event)
2496 {
2497         wmGesture *gesture= op->customdata;
2498         int sx, sy;
2499         
2500         switch(event->type) {
2501                 case MOUSEMOVE:
2502                 case INBETWEEN_MOUSEMOVE:
2503                         
2504                         wm_gesture_tag_redraw(C);
2505                         
2506                         wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2507
2508                         if(gesture->points == gesture->size) {
2509                                 short *old_lasso = gesture->customdata;
2510                                 gesture->customdata= MEM_callocN(2*sizeof(short)*(gesture->size + WM_LASSO_MIN_POINTS), "lasso points");
2511                                 memcpy(gesture->customdata, old_lasso, 2*sizeof(short)*gesture->size);
2512                                 gesture->size = gesture->size + WM_LASSO_MIN_POINTS;
2513                                 MEM_freeN(old_lasso);
2514                                 printf("realloc\n");
2515                         }
2516
2517                         {
2518                                 int x, y;
2519                                 short *lasso= gesture->customdata;
2520                                 
2521                                 lasso += (2 * gesture->points - 2);
2522                                 x = (event->x - sx - lasso[0]);
2523                                 y = (event->y - sy - lasso[1]);
2524                                 
2525                                 /* make a simple distance check to get a smoother lasso
2526                                    add only when at least 2 pixels between this and previous location */
2527                                 if((x*x+y*y) > 4) {
2528                                         lasso += 2;
2529                                         lasso[0] = event->x - sx;
2530                                         lasso[1] = event->y - sy;
2531                                         gesture->points++;
2532                                 }
2533                         }
2534                         break;
2535                         
2536                 case LEFTMOUSE:
2537                 case MIDDLEMOUSE:
2538                 case RIGHTMOUSE:
2539                         if(event->val==KM_RELEASE) {    /* key release */
2540                                 gesture_lasso_apply(C, op);
2541                                 return OPERATOR_FINISHED;
2542                         }
2543                         break;
2544                 case ESCKEY:
2545                         wm_gesture_end(C, op);
2546                         return OPERATOR_CANCELLED;
2547         }
2548         return OPERATOR_RUNNING_MODAL;
2549 }
2550
2551 int WM_gesture_lines_modal(bContext *C, wmOperator *op, wmEvent *event)
2552 {
2553         return WM_gesture_lasso_modal(C, op, event);
2554 }
2555
2556 #if 0
2557 /* template to copy from */
2558
2559 static int gesture_lasso_exec(bContext *C, wmOperator *op)
2560 {
2561         RNA_BEGIN(op->ptr, itemptr, "path") {
2562                 float loc[2];
2563                 
2564                 RNA_float_get_array(&itemptr, "loc", loc);
2565                 printf("Location: %f %f\n", loc[0], loc[1]);
2566         }
2567         RNA_END;
2568         
2569         return OPERATOR_FINISHED;
2570 }
2571
2572 void WM_OT_lasso_gesture(wmOperatorType *ot)
2573 {
2574         PropertyRNA *prop;
2575         
2576         ot->name= "Lasso Gesture";
2577         ot->idname= "WM_OT_lasso_gesture";
2578         ot->description="Select objects within the lasso as you move the pointer";
2579         
2580         ot->invoke= WM_gesture_lasso_invoke;
2581         ot->modal= WM_gesture_lasso_modal;
2582         ot->exec= gesture_lasso_exec;
2583         
2584         ot->poll= WM_operator_winactive;
2585         
2586         prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
2587         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
2588 }
2589 #endif
2590
2591 /* *********************** straight line gesture ****************** */
2592
2593 static int straightline_apply(bContext *C, wmOperator *op)
2594 {
2595         wmGesture *gesture= op->customdata;
2596         rcti *rect= gesture->customdata;
2597         
2598         if(rect->xmin==rect->xmax && rect->ymin==rect->ymax)
2599                 return 0;
2600         
2601         /* operator arguments and storage. */
2602         RNA_int_set(op->ptr, "xstart", rect->xmin);
2603         RNA_int_set(op->ptr, "ystart", rect->ymin);
2604         RNA_int_set(op->ptr, "xend", rect->xmax);
2605         RNA_int_set(op->ptr, "yend", rect->ymax);
2606
2607         if(op->type->exec)
2608                 op->type->exec(C, op);
2609         
2610         return 1;
2611 }
2612
2613
2614 int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, wmEvent *event)
2615 {
2616         op->customdata= WM_gesture_new(C, event, WM_GESTURE_STRAIGHTLINE);
2617         
2618         /* add modal handler */
2619         WM_event_add_modal_handler(C, op);
2620         
2621         wm_gesture_tag_redraw(C);
2622         
2623         if( RNA_struct_find_property(op->ptr, "cursor") )
2624                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2625                 
2626         return OPERATOR_RUNNING_MODAL;
2627 }
2628
2629 int WM_gesture_straightline_modal(bContext *C, wmOperator *op, wmEvent *event)
2630 {
2631         wmGesture *gesture= op->customdata;
2632         rcti *rect= gesture->customdata;
2633         int sx, sy;
2634         
2635         if(event->type== MOUSEMOVE) {
2636                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2637                 
2638                 if(gesture->mode==0) {
2639                         rect->xmin= rect->xmax= event->x - sx;
2640                         rect->ymin= rect->ymax= event->y - sy;
2641                 }
2642                 else {
2643                         rect->xmax= event->x - sx;
2644                         rect->ymax= event->y - sy;
2645                         straightline_apply(C, op);
2646                 }
2647                 
2648                 wm_gesture_tag_redraw(C);
2649         }
2650         else if (event->type==EVT_MODAL_MAP) {
2651                 switch (event->val) {
2652                         case GESTURE_MODAL_BEGIN:
2653                                 if(gesture->mode==0) {
2654                                         gesture->mode= 1;
2655                                         wm_gesture_tag_redraw(C);
2656                                 }
2657                                 break;
2658                         case GESTURE_MODAL_SELECT:
2659                                 if(straightline_apply(C, op)) {
2660                                         wm_gesture_end(C, op);
2661                                         return OPERATOR_FINISHED;
2662                                 }
2663                                 wm_gesture_end(C, op);
2664                                 return OPERATOR_CANCELLED;
2665                                 break;
2666                                 
2667                         case GESTURE_MODAL_CANCEL:
2668                                 wm_gesture_end(C, op);
2669                                 return OPERATOR_CANCELLED;
2670                 }
2671                 
2672         }
2673
2674         return OPERATOR_RUNNING_MODAL;
2675 }
2676
2677 #if 0
2678 /* template to copy from */
2679 void WM_OT_straightline_gesture(wmOperatorType *ot)
2680 {
2681         PropertyRNA *prop;
2682         
2683         ot->name= "Straight Line Gesture";
2684         ot->idname= "WM_OT_straightline_gesture";
2685         ot->description="Draw a straight line as you move the pointer";
2686         
2687         ot->invoke= WM_gesture_straightline_invoke;
2688         ot->modal= WM_gesture_straightline_modal;
2689         ot->exec= gesture_straightline_exec;
2690         
2691         ot->poll= WM_operator_winactive;
2692         
2693         WM_operator_properties_gesture_straightline(ot, 0);
2694 }
2695 #endif
2696
2697 /* *********************** radial control ****************** */
2698
2699 static const int WM_RADIAL_CONTROL_DISPLAY_SIZE = 200;
2700
2701 typedef struct {
2702         PropertyType type;
2703         PropertySubType subtype;
2704         PointerRNA ptr, col_ptr, fill_col_ptr, rot_ptr, zoom_ptr, image_id_ptr;
2705         PropertyRNA *prop, *col_prop, *fill_col_prop, *rot_prop, *zoom_prop;
2706         StructRNA *image_id_srna;
2707         float initial_value, current_value, min_value, max_value;
2708         int initial_mouse[2];
2709         unsigned int gltex;
2710         ListBase orig_paintcursors;
2711         void *cursor;
2712 } RadialControl;
2713
2714 static void radial_control_set_initial_mouse(RadialControl *rc, wmEvent *event)
2715 {
2716         float d[2] = {0, 0};
2717         float zoom[2] = {1, 1};
2718
2719         rc->initial_mouse[0]= event->x;
2720         rc->initial_mouse[1]= event->y;
2721
2722         switch(rc->subtype) {
2723         case PROP_DISTANCE:
2724                 d[0] = rc->initial_value;
2725                 break;
2726         case PROP_FACTOR:
2727                 d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * (1 - rc->initial_value);
2728                 break;
2729         case PROP_ANGLE:
2730                 d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * cos(rc->initial_value);
2731                 d[1] = WM_RADIAL_CONTROL_DISPLAY_SIZE * sin(rc->initial_value);
2732                 break;
2733         default:
2734                 return;
2735         }
2736
2737         if(rc->zoom_prop) {
2738                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2739                 d[0] *= zoom[0];
2740                 d[1] *= zoom[1];
2741         }
2742
2743         rc->initial_mouse[0]-= d[0];
2744         rc->initial_mouse[1]-= d[1];
2745 }
2746
2747 static void radial_control_set_tex(RadialControl *rc)
2748 {
2749         ImBuf *ibuf;
2750
2751         switch(RNA_type_to_ID_code(rc->image_id_ptr.type)) {
2752         case ID_BR:
2753                 if((ibuf = brush_gen_radial_control_imbuf(rc->image_id_ptr.data))) {
2754                         glGenTextures(1, &rc->gltex);
2755                         glBindTexture(GL_TEXTURE_2D, rc->gltex);
2756                         glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, ibuf->x, ibuf->y, 0,
2757                                      GL_ALPHA, GL_FLOAT, ibuf->rect_float);
2758                         MEM_freeN(ibuf->rect_float);
2759                         MEM_freeN(ibuf);
2760                 }
2761                 break;
2762         default:
2763                 break;
2764         }
2765 }
2766
2767 static void radial_control_paint_tex(RadialControl *rc, float radius, float alpha)
2768 {
2769         float col[3] = {0, 0, 0};
2770         float rot;
2771
2772         /* set fill color */
2773         if(rc->fill_col_prop)
2774                 RNA_property_float_get_array(&rc->fill_col_ptr, rc->fill_col_prop, col);
2775         glColor4f(col[0], col[1], col[2], alpha);
2776
2777         if(rc->gltex) {
2778                 glBindTexture(GL_TEXTURE_2D, rc->gltex);
2779
2780                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2781                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2782
2783                 /* set up rotation if available */
2784                 if(rc->rot_prop) {
2785                         rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop);
2786                         glPushMatrix();
2787                         glRotatef(RAD2DEGF(rot), 0, 0, 1);
2788                 }
2789
2790                 /* draw textured quad */
2791                 glEnable(GL_TEXTURE_2D);
2792                 glBegin(GL_QUADS);
2793                 glTexCoord2f(0,0);
2794                 glVertex2f(-radius, -radius);
2795                 glTexCoord2f(1,0);
2796                 glVertex2f(radius, -radius);
2797                 glTexCoord2f(1,1);
2798                 glVertex2f(radius, radius);
2799                 glTexCoord2f(0,1);
2800                 glVertex2f(-radius, radius);
2801                 glEnd();
2802                 glDisable(GL_TEXTURE_2D);
2803
2804                 /* undo rotation */
2805                 if(rc->rot_prop)
2806                         glPopMatrix();
2807         }
2808         else {
2809                 /* flat color if no texture available */
2810                 glutil_draw_filled_arc(0, M_PI * 2, radius, 40);
2811         }
2812 }
2813
2814 static void radial_control_paint_cursor(bContext *C, int x, int y, void *customdata)
2815 {
2816         RadialControl *rc = customdata;
2817         ARegion *ar = CTX_wm_region(C);
2818         float r1=0.0f, r2=0.0f, tex_radius, alpha;
2819         float zoom[2], col[3] = {1, 1, 1};
2820
2821         switch(rc->subtype) {
2822         case PROP_DISTANCE:
2823                 r1= rc->current_value;
2824                 r2= rc->initial_value;
2825                 tex_radius= r1;
2826                 alpha = 0.75;
2827                 break;
2828         case PROP_FACTOR:
2829                 r1= (1 - rc->current_value) * WM_RADIAL_CONTROL_DISPLAY_SIZE;
2830                 r2= tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE;
2831                 alpha = rc->current_value / 2 + 0.5;
2832                 break;
2833         case PROP_ANGLE:
2834                 r1= r2= tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE;
2835                 alpha = 0.75;
2836                 break;
2837         default:
2838                 break;
2839         }
2840
2841         /* Keep cursor in the original place */
2842         x = rc->initial_mouse[0] - ar->winrct.xmin;
2843         y = rc->initial_mouse[1] - ar->winrct.ymin;
2844         glTranslatef((float)x, (float)y, 0.0f);
2845
2846         glEnable(GL_BLEND);
2847         glEnable(GL_LINE_SMOOTH);
2848
2849         /* apply zoom if available */
2850         if(rc->zoom_prop) {
2851                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2852                 glScalef(zoom[0], zoom[1], 1);
2853         }
2854
2855         /* draw rotated texture */
2856         radial_control_paint_tex(rc, tex_radius, alpha);
2857
2858         /* set line color */
2859         if(rc->col_prop)
2860                 RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col);
2861         glColor4f(col[0], col[1], col[2], 0.5);
2862
2863         if(rc->subtype == PROP_ANGLE) {
2864                 glPushMatrix();
2865                 /* draw original angle line */
2866                 glRotatef(RAD2DEGF(rc->initial_value), 0, 0, 1);
2867                 fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
2868                 /* draw new angle line */
2869                 glRotatef(RAD2DEGF(rc->current_value - rc->initial_value), 0, 0, 1);
2870                 fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
2871                 glPopMatrix();
2872         }
2873
2874         /* draw circles on top */
2875         glutil_draw_lined_arc(0.0, (float)(M_PI*2.0), r1, 40);
2876         glutil_draw_lined_arc(0.0, (float)(M_PI*2.0), r2, 40);
2877
2878         glDisable(GL_BLEND);
2879         glDisable(GL_LINE_SMOOTH);
2880 }
2881
2882 /* attempt to retrieve the rna pointer/property from an rna path;
2883    returns 0 for failure, 1 for success, and also 1 if property is not
2884    set */
2885 static int radial_control_get_path(PointerRNA *ctx_ptr, wmOperator *op,
2886                                    const char *name, PointerRNA *r_ptr,
2887                                    PropertyRNA **r_prop, int req_float,
2888                                    int req_length, int allow_missing)
2889 {
2890         PropertyRNA *unused_prop;
2891         int len;
2892         char *str;
2893
2894         /* get an rna string path from the operator's properties */
2895         if(!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0)))
2896                 return 1;
2897
2898         if(str[0] == '\0') {
2899                 MEM_freeN(str);
2900                 return 1;
2901         }
2902
2903         if(!r_prop)
2904                 r_prop = &unused_prop;
2905
2906         /* get rna from path */
2907         if(!RNA_path_resolve(ctx_ptr, str, r_ptr, r_prop)) {
2908                 MEM_freeN(str);
2909                 if(allow_missing)
2910                         return 1;
2911                 else {
2912                         BKE_reportf(op->reports, RPT_ERROR, "Couldn't resolve path %s", name);
2913                         return 0;
2914                 }
2915         }
2916
2917         /* if property is expected to be a float, check its type */
2918         if(req_float) {
2919                 if(!(*r_prop) || (RNA_property_type(*r_prop) != PROP_FLOAT)) {
2920                         MEM_freeN(str);
2921                         BKE_reportf(op->reports, RPT_ERROR,
2922                                     "Property from path %s is not a float", name);
2923                         return 0;
2924                 }
2925         }
2926         
2927         /* check property's array length */
2928         if(*r_prop && (len = RNA_property_array_length(r_ptr, *r_prop)) != req_length) {
2929                 MEM_freeN(str);
2930                 BKE_reportf(op->reports, RPT_ERROR,
2931                             "Property from path %s has length %d instead of %d",
2932                             name, len, req_length);
2933                 return 0;
2934         }
2935
2936         /* success */
2937         MEM_freeN(str);
2938         return 1;
2939 }
2940
2941 /* initialize the rna pointers and properties using rna paths */
2942 static int radial_control_get_properties(bContext *C, wmOperator *op)
2943 {
2944         RadialControl *rc = op->customdata;
2945         PointerRNA ctx_ptr;
2946
2947         RNA_pointer_create(NULL, &RNA_Context, C, &ctx_ptr);
2948
2949         if(!radial_control_get_path(&ctx_ptr, op, "data_path", &rc->ptr, &rc->prop, 0, 0, 0))
2950                 return 0;
2951
2952         /* data path is required */
2953         if(!rc->prop)
2954                 return 0;
2955         
2956         if(!radial_control_get_path(&ctx_ptr, op, "rotation_path", &rc->rot_ptr, &rc->rot_prop, 1, 0, 0))
2957                 return 0;
2958         if(!radial_control_get_path(&ctx_ptr, op, "color_path", &rc->col_ptr, &rc->col_prop, 1, 3, 0))
2959                 return 0;
2960         if(!radial_control_get_path(&ctx_ptr, op, "fill_color_path", &rc->fill_col_ptr, &rc->fill_col_prop, 1, 3, 0))
2961                 return 0;
2962         
2963         /* slightly ugly; allow this property to not resolve
2964            correctly. needed because 3d texture paint shares the same
2965            keymap as 2d image paint */
2966         if(!radial_control_get_path(&ctx_ptr, op, "zoom_path", &rc->zoom_ptr, &rc->zoom_prop, 1, 2, 1))
2967                 return 0;
2968         
2969         if(!radial_control_get_path(&ctx_ptr, op, "image_id", &rc->image_id_ptr, NULL, 0, 0, 0))
2970                 return 0;
2971         else if(rc->image_id_ptr.data) {
2972                 /* extra check, pointer must be to an ID */
2973                 if(!RNA_struct_is_ID(rc->image_id_ptr.type)) {
2974                         BKE_report(op->reports, RPT_ERROR,
2975                                    "Pointer from path image_id is not an ID");
2976                         return 0;
2977                 }
2978         }
2979
2980         return 1;
2981 }
2982
2983 static int radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event)
2984 {
2985         wmWindowManager *wm;
2986         RadialControl *rc;
2987         int min_value_int, max_value_int, step_int;
2988         float step_float, precision;
2989
2990         if(!(op->customdata = rc = MEM_callocN(sizeof(RadialControl), "RadialControl")))
2991                 return OPERATOR_CANCELLED;
2992
2993         if(!radial_control_get_properties(C, op)) {
2994                 MEM_freeN(rc);
2995                 return OPERATOR_CANCELLED;
2996         }
2997
2998         /* get type, initial, min, and max values of the property */
2999         switch((rc->type = RNA_property_type(rc->prop))) {
3000         case PROP_INT:
3001                 rc->initial_value = RNA_property_int_get(&rc->ptr, rc->prop);
3002                 RNA_property_int_ui_range(&rc->ptr, rc->prop, &min_value_int,
3003                                           &max_value_int, &step_int);
3004                 rc->min_value = min_value_int;
3005                 rc->max_value = max_value_int;
3006                 break;
3007         case PROP_FLOAT:
3008                 rc->initial_value = RNA_property_float_get(&rc->ptr, rc->prop);
3009                 RNA_property_float_ui_range(&rc->ptr, rc->prop, &rc->min_value,
3010                                             &rc->max_value, &step_float, &precision);
3011                 break;
3012         default:
3013                 BKE_report(op->reports, RPT_ERROR, "Property must be an integer or a float");
3014                 MEM_freeN(rc);
3015                 return OPERATOR_CANCELLED;
3016         }
3017
3018         /* get subtype of property */
3019         rc->subtype = RNA_property_subtype(rc->prop);
3020         if(!ELEM3(rc->subtype, PROP_DISTANCE, PROP_FACTOR, PROP_ANGLE)) {
3021                 BKE_report(op->reports, RPT_ERROR, "Property must be a distance, a factor, or an angle");
3022                 MEM_freeN(rc);
3023                 return OPERATOR_CANCELLED;
3024         }
3025                 
3026         rc->current_value = rc->initial_value;
3027         radial_control_set_initial_mouse(rc, event);
3028         radial_control_set_tex(rc);
3029
3030         /* temporarily disable other paint cursors */
3031         wm = CTX_wm_manager(C);
3032         rc->orig_paintcursors = wm->paintcursors;
3033         wm->paintcursors.first = wm->paintcursors.last = NULL;
3034
3035         /* add radial control paint cursor */
3036         rc->cursor = WM_paint_cursor_activate(wm, op->type->poll,
3037                                               radial_control_paint_cursor, rc);
3038
3039         WM_event_add_modal_handler(C, op);
3040
3041         return OPERATOR_RUNNING_MODAL;
3042 }
3043
3044 static void radial_control_set_value(RadialControl *rc, float val)
3045 {
3046         switch(rc->type) {
3047         case PROP_INT:
3048                 RNA_property_int_set(&rc->ptr, rc->prop, val);
3049                 break;
3050         case PROP_FLOAT:
3051                 RNA_property_float_set(&rc->ptr, rc->prop, val);
3052                 break;
3053         default:
3054                 break;
3055         }
3056 }
3057
3058 static int radial_control_modal(bContext *C, wmOperator *op, wmEvent *event)
3059 {
3060         RadialControl *rc = op->customdata;
3061         wmWindowManager *wm;
3062         float new_value, dist, zoom[2];
3063         float delta[2], snap, ret = OPERATOR_RUNNING_MODAL;
3064
3065         /* TODO: fix hardcoded events */
3066
3067         snap = event->ctrl;
3068
3069         switch(event->type) {
3070         case MOUSEMOVE:
3071                 delta[0]= rc->initial_mouse[0] - event->x;
3072                 delta[1]= rc->initial_mouse[1] - event->y;
3073
3074                 if(rc->zoom_prop) {
3075                         RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
3076                         delta[0] /= zoom[0];
3077                         delta[1] /= zoom[1];
3078                 }
3079
3080                 dist= sqrt(delta[0]*delta[0]+delta[1]*delta[1]);
3081
3082                 /* calculate new value and apply snapping  */
3083                 switch(rc->subtype) {
3084                 case PROP_DISTANCE:
3085                         new_value = dist;
3086                         if(snap) new_value = ((int)new_value + 5) / 10*10;
3087                         break;
3088                 case PROP_FACTOR:
3089                         new_value = 1 - dist / WM_RADIAL_CONTROL_DISPLAY_SIZE;
3090                         if(snap) new_value = ((int)ceil(new_value * 10.f) * 10.0f) / 100.f;
3091                         break;
3092                 case PROP_ANGLE:
3093                         new_value = atan2(delta[1], delta[0]) + M_PI;
3094                         if(snap) new_value = DEG2RADF(((int)RAD2DEGF(new_value) + 5) / 10*10);
3095                         break;
3096                 default:
3097                         break;
3098                 }
3099
3100                 /* clamp and update */
3101                 CLAMP(new_value, rc->min_value, rc->max_value);
3102                 radial_control_set_value(rc, new_value);
3103                 rc->current_value = new_value;
3104                 break;
3105
3106         case ESCKEY:
3107         case RIGHTMOUSE:
3108                 /* cancelled; restore original value */
3109                 radial_control_set_value(rc, rc->initial_value);
3110                 ret = OPERATOR_CANCELLED;
3111                 break;
3112                 
3113         case LEFTMOUSE:
3114         case PADENTER:
3115                 /* done; value already set */
3116                 ret = OPERATOR_FINISHED;
3117                 break;
3118         }
3119
3120         ED_region_tag_redraw(CTX_wm_region(C));
3121
3122         if(ret != OPERATOR_RUNNING_MODAL) {
3123                 wm = CTX_wm_manager(C);
3124
3125                 WM_paint_cursor_end(wm, rc->cursor);
3126
3127                 /* restore original paint cursors */
3128                 wm->paintcursors = rc->orig_paintcursors;
3129
3130                 /* not sure if this is a good notifier to use;
3131                    intended purpose is to update the UI so that the
3132                    new value is displayed in sliders/numfields */
3133                 WM_event_add_notifier(C, NC_WINDOW, NULL);
3134
3135                 glDeleteTextures(1, &rc->gltex);
3136
3137                 MEM_freeN(rc);
3138         }
3139
3140         return ret;
3141 }
3142
3143 static void WM_OT_radial_control(wmOperatorType *ot)
3144 {
3145         ot->name= "Radial Control";
3146         ot->idname= "WM_OT_radial_control";
3147
3148         ot->invoke= radial_control_invoke;
3149         ot->modal= radial_control_modal;
3150
3151         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
3152
3153         /* all paths relative to the context */
3154         RNA_def_string(ot->srna, "data_path", "", 0, "Data Path", "Path of property to be set by the radial control.");
3155         RNA_def_string(ot->srna, "rotation_path", "", 0, "Rotation Path", "Path of property used to rotate the texture display.");
3156         RNA_def_string(ot->srna, "color_path", "", 0, "Color Path", "Path of property used to set the color of the control.");
3157         RNA_def_string(ot->srna, "fill_color_path", "", 0, "Fill Color Path", "Path of property used to set the fill color of the control.");
3158         RNA_def_string(ot->srna, "zoom_path", "", 0, "Zoom Path", "Path of property used to set the zoom level for the control.");
3159         RNA_def_string(ot->srna, "image_id", "", 0, "Image ID", "Path of ID that is used to generate an image for the control.");
3160 }
3161
3162 /* ************************** timer for testing ***************** */
3163
3164 /* uses no type defines, fully local testing function anyway... ;) */
3165
3166 static void redraw_timer_window_swap(bContext *C)
3167 {
3168         wmWindow *win= CTX_wm_window(C);
3169         ScrArea *sa;
3170
3171         for(sa= CTX_wm_screen(C)->areabase.first; sa; sa= sa->next)
3172                 ED_area_tag_redraw(sa);
3173         wm_draw_update(C);
3174
3175         CTX_wm_window_set(C, win);      /* XXX context manipulation warning! */
3176 }
3177
3178 static EnumPropertyItem redraw_timer_type_items[] = {
3179         {0, "DRAW", 0, "Draw Region", "Draw Region"},
3180         {1, "DRAW_SWAP", 0, "Draw Region + Swap", "Draw Region and Swap"},
3181         {2, "DRAW_WIN", 0, "Draw Window", "Draw Window"},
3182         {3, "DRAW_WIN_SWAP", 0, "Draw Window + Swap", "Draw Window and Swap"},
3183         {4, "ANIM_STEP", 0, "Anim Step", "Animation Steps"},
3184         {5, "ANIM_PLAY", 0, "Anim Play", "Animation Playback"},
3185         {6, "UNDO", 0, "Undo/Redo", "Undo/Redo"},
3186         {0, NULL, 0, NULL, NULL}};
3187
3188 static int redraw_timer_exec(bContext *C, wmOperator *op)
3189 {
3190         ARegion *ar= CTX_wm_region(C);
3191         double stime= PIL_check_seconds_timer();
3192         int type = RNA_enum_get(op->ptr, "type");
3193         int iter = RNA_int_get(op->ptr, "iterations");
3194         int a;
3195         float time;
3196         const char *infostr= "";
3197         
3198         WM_cursor_wait(1);
3199
3200         for(a=0; a<iter; a++) {
3201                 if (type==0) {
3202                         if(ar)
3203                                 ED_region_do_draw(C, ar);
3204                 } 
3205                 else if (type==1) {
3206                         wmWindow *win= CTX_wm_window(C);
3207