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