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