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