Release schedule:
[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_buf[128];
1281         char revision_buf[128];
1282         extern char build_rev[];
1283         
1284         BLI_snprintf(version_buf, sizeof(version_buf),
1285                      "%d.%02d.%d", BLENDER_VERSION/100, BLENDER_VERSION%100, BLENDER_SUBVERSION);
1286         BLI_snprintf(revision_buf, sizeof(revision_buf), "r%s", build_rev);
1287         
1288         BLF_size(style->widgetlabel.uifont_id, style->widgetlabel.points, U.dpi);
1289         ver_width = (int)BLF_width(style->widgetlabel.uifont_id, version_buf) + 5;
1290         rev_width = (int)BLF_width(style->widgetlabel.uifont_id, revision_buf) + 5;
1291 #endif //WITH_BUILDINFO
1292
1293         block= uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
1294         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN);
1295         
1296         but= uiDefBut(block, BUT_IMAGE, 0, "", 0, 10, 501, 282, ibuf, 0.0, 0.0, 0, 0, ""); /* button owns the imbuf now */
1297         uiButSetFunc(but, wm_block_splash_close, block, NULL);
1298         uiBlockSetFunc(block, wm_block_splash_refreshmenu, block, NULL);
1299         
1300 #ifdef WITH_BUILDINFO   
1301         uiDefBut(block, LABEL, 0, version_buf, 494-ver_width, 282-24, ver_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
1302         uiDefBut(block, LABEL, 0, revision_buf, 494-rev_width, 282-36, rev_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
1303 #endif //WITH_BUILDINFO
1304         
1305         layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 10, 2, 480, 110, style);
1306         
1307         uiBlockSetEmboss(block, UI_EMBOSS);
1308         /* show the splash menu (containing interaction presets), using python */
1309         if (mt) {
1310                 Menu menu= {NULL};
1311                 menu.layout= layout;
1312                 menu.type= mt;
1313                 mt->draw(C, &menu);
1314
1315 //              wmWindowManager *wm= CTX_wm_manager(C);
1316 //              uiItemM(layout, C, "USERPREF_MT_keyconfigs", U.keyconfigstr, ICON_NONE);
1317         }
1318         
1319         uiBlockSetEmboss(block, UI_EMBOSSP);
1320         uiLayoutSetOperatorContext(layout, WM_OP_EXEC_REGION_WIN);
1321         
1322         split = uiLayoutSplit(layout, 0, 0);
1323         col = uiLayoutColumn(split, 0);
1324         uiItemL(col, "Links", ICON_NONE);
1325         uiItemStringO(col, IFACE_("Donations"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/blenderorg/blender-foundation/donation-payment");
1326         uiItemStringO(col, IFACE_("Credits"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/development/credits");
1327         uiItemStringO(col, IFACE_("Release Log"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/development/release-logs/blender-262");
1328         uiItemStringO(col, IFACE_("Manual"), ICON_URL, "WM_OT_url_open", "url", "http://wiki.blender.org/index.php/Doc:2.5/Manual");
1329         uiItemStringO(col, IFACE_("Blender Website"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org");
1330         uiItemStringO(col, IFACE_("User Community"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/community/user-community");
1331         if(strcmp(STRINGIFY(BLENDER_VERSION_CYCLE), "release")==0) {
1332                 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);
1333         }
1334         else {
1335                 BLI_snprintf(url, sizeof(url), "http://www.blender.org/documentation/blender_python_api_%d_%d_%d", BLENDER_VERSION/100, BLENDER_VERSION%100, BLENDER_SUBVERSION);
1336         }
1337         uiItemStringO(col, IFACE_("Python API Reference"), ICON_URL, "WM_OT_url_open", "url", url);
1338         uiItemL(col, "", ICON_NONE);
1339
1340         col = uiLayoutColumn(split, 0);
1341
1342         if(wm_resource_check_prev()) {
1343                 uiItemO(col, NULL, ICON_NEW, "WM_OT_copy_prev_settings");
1344                 uiItemS(col);
1345         }
1346
1347         uiItemL(col, IFACE_("Recent"), ICON_NONE);
1348         for(recent = G.recent_files.first, i=0; (i<5) && (recent); recent = recent->next, i++) {
1349                 uiItemStringO(col, BLI_path_basename(recent->filepath), ICON_FILE_BLEND, "WM_OT_open_mainfile", "filepath", recent->filepath);
1350         }
1351
1352         uiItemS(col);
1353         uiItemO(col, NULL, ICON_RECOVER_LAST, "WM_OT_recover_last_session");
1354         uiItemL(col, "", ICON_NONE);
1355         
1356         uiCenteredBoundsBlock(block, 0);
1357         uiEndBlock(C, block);
1358         
1359         return block;
1360 }
1361
1362 static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
1363 {
1364         uiPupBlock(C, wm_block_create_splash, NULL);
1365         
1366         return OPERATOR_FINISHED;
1367 }
1368
1369 static void WM_OT_splash(wmOperatorType *ot)
1370 {
1371         ot->name= "Splash Screen";
1372         ot->idname= "WM_OT_splash";
1373         ot->description= "Opens a blocking popup region with release info";
1374         
1375         ot->invoke= wm_splash_invoke;
1376         ot->poll= WM_operator_winactive;
1377 }
1378
1379
1380 /* ***************** Search menu ************************* */
1381 static void operator_call_cb(struct bContext *C, void *UNUSED(arg1), void *arg2)
1382 {
1383         wmOperatorType *ot= arg2;
1384         
1385         if(ot)
1386                 WM_operator_name_call(C, ot->idname, WM_OP_INVOKE_DEFAULT, NULL);
1387 }
1388
1389 static void operator_search_cb(const struct bContext *C, void *UNUSED(arg), const char *str, uiSearchItems *items)
1390 {
1391         GHashIterator *iter= WM_operatortype_iter();
1392
1393         for( ; !BLI_ghashIterator_isDone(iter); BLI_ghashIterator_step(iter)) {
1394                 wmOperatorType *ot= BLI_ghashIterator_getValue(iter);
1395
1396                 if((ot->flag & OPTYPE_INTERNAL) && (G.f & G_DEBUG) == 0)
1397                         continue;
1398
1399                 if(BLI_strcasestr(ot->name, str)) {
1400                         if(WM_operator_poll((bContext*)C, ot)) {
1401                                 char name[256];
1402                                 int len= strlen(ot->name);
1403                                 
1404                                 /* display name for menu, can hold hotkey */
1405                                 BLI_strncpy(name, ot->name, sizeof(name));
1406                                 
1407                                 /* check for hotkey */
1408                                 if (len < sizeof(name) - 6) {
1409                                         if (WM_key_event_operator_string(C, ot->idname, WM_OP_EXEC_DEFAULT, NULL, TRUE,
1410                                                                         &name[len+1], sizeof(name)-len-1))
1411                                         {
1412                                                 name[len]= '|';
1413                                         }
1414                                 }
1415                                 
1416                                 if(0==uiSearchItemAdd(items, name, ot, 0))
1417                                         break;
1418                         }
1419                 }
1420         }
1421         BLI_ghashIterator_free(iter);
1422 }
1423
1424 static uiBlock *wm_block_search_menu(bContext *C, ARegion *ar, void *UNUSED(arg_op))
1425 {
1426         static char search[256]= "";
1427         wmEvent event;
1428         wmWindow *win= CTX_wm_window(C);
1429         uiBlock *block;
1430         uiBut *but;
1431         
1432         block= uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
1433         uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
1434         
1435         but= uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, 9*UI_UNIT_X, UI_UNIT_Y, 0, 0, "");
1436         uiButSetSearchFunc(but, operator_search_cb, NULL, operator_call_cb, NULL);
1437         
1438         /* fake button, it holds space for search items */
1439         uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxhHeight(), 9*UI_UNIT_X, uiSearchBoxhHeight(), NULL, 0, 0, 0, 0, NULL);
1440         
1441         uiPopupBoundsBlock(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
1442         uiEndBlock(C, block);
1443         
1444         event= *(win->eventstate);      /* XXX huh huh? make api call */
1445         event.type= EVT_BUT_OPEN;
1446         event.val= KM_PRESS;
1447         event.customdata= but;
1448         event.customdatafree= FALSE;
1449         wm_event_add(win, &event);
1450         
1451         return block;
1452 }
1453
1454 static int wm_search_menu_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
1455 {
1456         return OPERATOR_FINISHED;       
1457 }
1458
1459 static int wm_search_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1460 {
1461         uiPupBlock(C, wm_block_search_menu, op);
1462         
1463         return OPERATOR_CANCELLED;
1464 }
1465
1466 /* op->poll */
1467 static int wm_search_menu_poll(bContext *C)
1468 {
1469         if(CTX_wm_window(C)==NULL) {
1470                 return 0;
1471         }
1472         else {
1473                 ScrArea *sa= CTX_wm_area(C);
1474                 if(sa) {
1475                         if(sa->spacetype==SPACE_CONSOLE) return 0;  // XXX - so we can use the shortcut in the console
1476                         if(sa->spacetype==SPACE_TEXT) return 0;  // XXX - so we can use the spacebar in the text editor                 
1477                 }
1478                 else {
1479                         Object *editob= CTX_data_edit_object(C);
1480                         if(editob && editob->type==OB_FONT) return 0; // XXX - so we can use the spacebar for entering text
1481                 }
1482         }
1483         return 1;
1484 }
1485
1486 static void WM_OT_search_menu(wmOperatorType *ot)
1487 {
1488         ot->name= "Search Menu";
1489         ot->idname= "WM_OT_search_menu";
1490         
1491         ot->invoke= wm_search_menu_invoke;
1492         ot->exec= wm_search_menu_exec;
1493         ot->poll= wm_search_menu_poll;
1494 }
1495
1496 static int wm_call_menu_exec(bContext *C, wmOperator *op)
1497 {
1498         char idname[BKE_ST_MAXNAME];
1499         RNA_string_get(op->ptr, "name", idname);
1500
1501         uiPupMenuInvoke(C, idname);
1502
1503         return OPERATOR_CANCELLED;
1504 }
1505
1506 static void WM_OT_call_menu(wmOperatorType *ot)
1507 {
1508         ot->name= "Call Menu";
1509         ot->idname= "WM_OT_call_menu";
1510
1511         ot->exec= wm_call_menu_exec;
1512         ot->poll= WM_operator_winactive;
1513
1514         ot->flag= OPTYPE_INTERNAL;
1515
1516         RNA_def_string(ot->srna, "name", "", BKE_ST_MAXNAME, "Name", "Name of the menu");
1517 }
1518
1519 /* ************ window / screen operator definitions ************** */
1520
1521 /* this poll functions is needed in place of WM_operator_winactive
1522  * while it crashes on full screen */
1523 static int wm_operator_winactive_normal(bContext *C)
1524 {
1525         wmWindow *win= CTX_wm_window(C);
1526
1527         if(win==NULL || win->screen==NULL || win->screen->full != SCREENNORMAL)
1528                 return 0;
1529
1530         return 1;
1531 }
1532
1533 static void WM_OT_window_duplicate(wmOperatorType *ot)
1534 {
1535         ot->name= "Duplicate Window";
1536         ot->idname= "WM_OT_window_duplicate";
1537         ot->description="Duplicate the current Blender window";
1538                 
1539         ot->exec= wm_window_duplicate_exec;
1540         ot->poll= wm_operator_winactive_normal;
1541 }
1542
1543 static void WM_OT_save_homefile(wmOperatorType *ot)
1544 {
1545         ot->name= "Save User Settings";
1546         ot->idname= "WM_OT_save_homefile";
1547         ot->description="Make the current file the default .blend file";
1548                 
1549         ot->invoke= WM_operator_confirm;
1550         ot->exec= WM_write_homefile;
1551         ot->poll= WM_operator_winactive;
1552 }
1553
1554 static void WM_OT_read_homefile(wmOperatorType *ot)
1555 {
1556         ot->name= "Reload Start-Up File";
1557         ot->idname= "WM_OT_read_homefile";
1558         ot->description="Open the default file (doesn't save the current file)";
1559         
1560         ot->invoke= WM_operator_confirm;
1561         ot->exec= WM_read_homefile_exec;
1562         /* ommit poll to run in background mode */
1563 }
1564
1565 static void WM_OT_read_factory_settings(wmOperatorType *ot)
1566 {
1567         ot->name= "Load Factory Settings";
1568         ot->idname= "WM_OT_read_factory_settings";
1569         ot->description="Load default file and user preferences";
1570         
1571         ot->invoke= WM_operator_confirm;
1572         ot->exec= WM_read_homefile_exec;
1573         /* ommit poll to run in background mode */
1574 }
1575
1576 /* *************** open file **************** */
1577
1578 static void open_set_load_ui(wmOperator *op)
1579 {
1580         if(!RNA_struct_property_is_set(op->ptr, "load_ui"))
1581                 RNA_boolean_set(op->ptr, "load_ui", !(U.flag & USER_FILENOUI));
1582 }
1583
1584 static void open_set_use_scripts(wmOperator *op)
1585 {
1586         if(!RNA_struct_property_is_set(op->ptr, "use_scripts")) {
1587                 /* use G_SCRIPT_AUTOEXEC rather than the userpref because this means if
1588                  * the flag has been disabled from the command line, then opening
1589                  * from the menu wont enable this setting. */
1590                 RNA_boolean_set(op->ptr, "use_scripts", (G.f & G_SCRIPT_AUTOEXEC));
1591         }
1592 }
1593
1594 static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1595 {
1596         const char *openname= G.main->name;
1597
1598         if(CTX_wm_window(C) == NULL) {
1599                 /* in rare cases this could happen, when trying to invoke in background
1600                  * mode on load for example. Don't use poll for this because exec()
1601                  * can still run without a window */
1602                 BKE_report(op->reports, RPT_ERROR, "Context window not set");
1603                 return OPERATOR_CANCELLED;
1604         }
1605
1606         /* if possible, get the name of the most recently used .blend file */
1607         if (G.recent_files.first) {
1608                 struct RecentFile *recent = G.recent_files.first;
1609                 openname = recent->filepath;
1610         }
1611
1612         RNA_string_set(op->ptr, "filepath", openname);
1613         open_set_load_ui(op);
1614         open_set_use_scripts(op);
1615
1616         WM_event_add_fileselect(C, op);
1617
1618         return OPERATOR_RUNNING_MODAL;
1619 }
1620
1621 static int wm_open_mainfile_exec(bContext *C, wmOperator *op)
1622 {
1623         char path[FILE_MAX];
1624
1625         RNA_string_get(op->ptr, "filepath", path);
1626         open_set_load_ui(op);
1627         open_set_use_scripts(op);
1628
1629         if(RNA_boolean_get(op->ptr, "load_ui"))
1630                 G.fileflags &= ~G_FILE_NO_UI;
1631         else
1632                 G.fileflags |= G_FILE_NO_UI;
1633                 
1634         if(RNA_boolean_get(op->ptr, "use_scripts"))
1635                 G.f |= G_SCRIPT_AUTOEXEC;
1636         else
1637                 G.f &= ~G_SCRIPT_AUTOEXEC;
1638         
1639         // XXX wm in context is not set correctly after WM_read_file -> crash
1640         // do it before for now, but is this correct with multiple windows?
1641         WM_event_add_notifier(C, NC_WINDOW, NULL);
1642
1643         WM_read_file(C, path, op->reports);
1644         
1645         return OPERATOR_FINISHED;
1646 }
1647
1648 static void WM_OT_open_mainfile(wmOperatorType *ot)
1649 {
1650         ot->name= "Open Blender File";
1651         ot->idname= "WM_OT_open_mainfile";
1652         ot->description="Open a Blender file";
1653         
1654         ot->invoke= wm_open_mainfile_invoke;
1655         ot->exec= wm_open_mainfile_exec;
1656         /* ommit window poll so this can work in background mode */
1657         
1658         WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY);
1659
1660         RNA_def_boolean(ot->srna, "load_ui", 1, "Load UI", "Load user interface setup in the .blend file");
1661         RNA_def_boolean(ot->srna, "use_scripts", 1, "Trusted Source", "Allow blend file execute scripts automatically, default available from system preferences");
1662 }
1663
1664 /* **************** link/append *************** */
1665
1666 int wm_link_append_poll(bContext *C)
1667 {
1668         if(WM_operator_winactive(C)) {
1669                 /* linking changes active object which is pretty useful in general,
1670                    but which totally confuses edit mode (i.e. it becoming not so obvious
1671                    to leave from edit mode and inwalid tools in toolbar might be displayed)
1672                    so disable link/append when in edit mode (sergey) */
1673                 if(CTX_data_edit_object(C))
1674                         return 0;
1675
1676                 return 1;
1677         }
1678
1679         return 0;
1680 }
1681
1682 static int wm_link_append_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1683 {
1684         if(RNA_struct_property_is_set(op->ptr, "filepath")) {
1685                 return WM_operator_call_notest(C, op);
1686         } 
1687         else {
1688                 /* XXX TODO solve where to get last linked library from */
1689                 if(G.lib[0] != '\0') {
1690                         RNA_string_set(op->ptr, "filepath", G.lib);
1691                 }
1692                 else if(G.relbase_valid) {
1693                         char path[FILE_MAX];
1694                         BLI_strncpy(path, G.main->name, sizeof(G.main->name));
1695                         BLI_parent_dir(path);
1696                         RNA_string_set(op->ptr, "filepath", path);
1697                 }
1698                 WM_event_add_fileselect(C, op);
1699                 return OPERATOR_RUNNING_MODAL;
1700         }
1701 }
1702
1703 static short wm_link_append_flag(wmOperator *op)
1704 {
1705         short flag= 0;
1706
1707         if(RNA_boolean_get(op->ptr, "autoselect")) flag |= FILE_AUTOSELECT;
1708         if(RNA_boolean_get(op->ptr, "active_layer")) flag |= FILE_ACTIVELAY;
1709         if(RNA_boolean_get(op->ptr, "relative_path")) flag |= FILE_RELPATH;
1710         if(RNA_boolean_get(op->ptr, "link")) flag |= FILE_LINK;
1711         if(RNA_boolean_get(op->ptr, "instance_groups")) flag |= FILE_GROUP_INSTANCE;
1712
1713         return flag;
1714 }
1715
1716 static int wm_link_append_exec(bContext *C, wmOperator *op)
1717 {
1718         Main *bmain= CTX_data_main(C);
1719         Scene *scene= CTX_data_scene(C);
1720         Main *mainl= NULL;
1721         BlendHandle *bh;
1722         PropertyRNA *prop;
1723         char name[FILE_MAX], dir[FILE_MAX], libname[FILE_MAX], group[GROUP_MAX];
1724         int idcode, totfiles=0;
1725         short flag;
1726
1727         RNA_string_get(op->ptr, "filename", name);
1728         RNA_string_get(op->ptr, "directory", dir);
1729
1730         /* test if we have a valid data */
1731         if(BLO_is_a_library(dir, libname, group) == 0) {
1732                 BKE_report(op->reports, RPT_ERROR, "Not a library");
1733                 return OPERATOR_CANCELLED;
1734         }
1735         else if(group[0] == 0) {
1736                 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1737                 return OPERATOR_CANCELLED;
1738         }
1739         else if(BLI_path_cmp(bmain->name, libname) == 0) {
1740                 BKE_report(op->reports, RPT_ERROR, "Cannot use current file as library");
1741                 return OPERATOR_CANCELLED;
1742         }
1743
1744         /* check if something is indicated for append/link */
1745         prop = RNA_struct_find_property(op->ptr, "files");
1746         if(prop) {
1747                 totfiles= RNA_property_collection_length(op->ptr, prop);
1748                 if(totfiles == 0) {
1749                         if(name[0] == '\0') {
1750                                 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1751                                 return OPERATOR_CANCELLED;
1752                         }
1753                 }
1754         }
1755         else if(name[0] == '\0') {
1756                 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1757                 return OPERATOR_CANCELLED;
1758         }
1759
1760         bh = BLO_blendhandle_from_file(libname, op->reports);
1761
1762         if(bh == NULL) {
1763                 /* unlikely since we just browsed it, but possible
1764                  * error reports will have been made by BLO_blendhandle_from_file() */
1765                 return OPERATOR_CANCELLED;
1766         }
1767
1768
1769         /* from here down, no error returns */
1770
1771         idcode = BKE_idcode_from_name(group);
1772
1773         /* now we have or selected, or an indicated file */
1774         if(RNA_boolean_get(op->ptr, "autoselect"))
1775                 scene_deselect_all(scene);
1776
1777         
1778         flag = wm_link_append_flag(op);
1779
1780         /* sanity checks for flag */
1781         if(scene->id.lib && (flag & FILE_GROUP_INSTANCE)) {
1782                 /* TODO, user never gets this message */
1783                 BKE_reportf(op->reports, RPT_WARNING, "Scene '%s' is linked, group instance disabled", scene->id.name+2);
1784                 flag &= ~FILE_GROUP_INSTANCE;
1785         }
1786
1787
1788         /* tag everything, all untagged data can be made local
1789          * its also generally useful to know what is new
1790          *
1791          * take extra care flag_all_listbases_ids(LIB_LINK_TAG, 0) is called after! */
1792         flag_all_listbases_ids(LIB_PRE_EXISTING, 1);
1793
1794         /* here appending/linking starts */
1795         mainl = BLO_library_append_begin(bmain, &bh, libname);
1796         if(totfiles == 0) {
1797                 BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
1798         }
1799         else {
1800                 RNA_BEGIN(op->ptr, itemptr, "files") {
1801                         RNA_string_get(&itemptr, "name", name);
1802                         BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
1803                 }
1804                 RNA_END;
1805         }
1806         BLO_library_append_end(C, mainl, &bh, idcode, flag);
1807         
1808         /* mark all library linked objects to be updated */
1809         recalc_all_library_objects(bmain);
1810
1811         /* append, rather than linking */
1812         if((flag & FILE_LINK)==0) {
1813                 Library *lib= BLI_findstring(&bmain->library, libname, offsetof(Library, filepath));
1814                 if(lib) BKE_library_make_local(bmain, lib, 1);
1815                 else    BLI_assert(!"cant find name of just added library!");
1816         }
1817
1818         /* important we unset, otherwise these object wont
1819          * link into other scenes from this blend file */
1820         flag_all_listbases_ids(LIB_PRE_EXISTING, 0);
1821
1822         /* recreate dependency graph to include new objects */
1823         DAG_scene_sort(bmain, scene);
1824         DAG_ids_flush_update(bmain, 0);
1825
1826         BLO_blendhandle_close(bh);
1827
1828         /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
1829         BLI_strncpy(G.lib, dir, FILE_MAX);
1830
1831         WM_event_add_notifier(C, NC_WINDOW, NULL);
1832
1833         return OPERATOR_FINISHED;
1834 }
1835
1836 static void WM_OT_link_append(wmOperatorType *ot)
1837 {
1838         ot->name= "Link/Append from Library";
1839         ot->idname= "WM_OT_link_append";
1840         ot->description= "Link or Append from a Library .blend file";
1841         
1842         ot->invoke= wm_link_append_invoke;
1843         ot->exec= wm_link_append_exec;
1844         ot->poll= wm_link_append_poll;
1845         
1846         ot->flag |= OPTYPE_UNDO;
1847
1848         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);
1849         
1850         RNA_def_boolean(ot->srna, "link", 1, "Link", "Link the objects or datablocks rather than appending");
1851         RNA_def_boolean(ot->srna, "autoselect", 1, "Select", "Select the linked objects");
1852         RNA_def_boolean(ot->srna, "active_layer", 1, "Active Layer", "Put the linked objects on the active layer");
1853         RNA_def_boolean(ot->srna, "instance_groups", 1, "Instance Groups", "Create instances for each group as a DupliGroup");
1854 }       
1855
1856 /* *************** recover last session **************** */
1857
1858 static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
1859 {
1860         char filename[FILE_MAX];
1861
1862         G.fileflags |= G_FILE_RECOVER;
1863
1864         // XXX wm in context is not set correctly after WM_read_file -> crash
1865         // do it before for now, but is this correct with multiple windows?
1866         WM_event_add_notifier(C, NC_WINDOW, NULL);
1867
1868         /* load file */
1869         BLI_make_file_string("/", filename, BLI_temporary_dir(), "quit.blend");
1870         WM_read_file(C, filename, op->reports);
1871
1872         G.fileflags &= ~G_FILE_RECOVER;
1873         return OPERATOR_FINISHED;
1874 }
1875
1876 static void WM_OT_recover_last_session(wmOperatorType *ot)
1877 {
1878         ot->name= "Recover Last Session";
1879         ot->idname= "WM_OT_recover_last_session";
1880         ot->description="Open the last closed file (\"quit.blend\")";
1881         
1882         ot->exec= wm_recover_last_session_exec;
1883         ot->poll= WM_operator_winactive;
1884 }
1885
1886 /* *************** recover auto save **************** */
1887
1888 static int wm_recover_auto_save_exec(bContext *C, wmOperator *op)
1889 {
1890         char path[FILE_MAX];
1891
1892         RNA_string_get(op->ptr, "filepath", path);
1893
1894         G.fileflags |= G_FILE_RECOVER;
1895
1896         // XXX wm in context is not set correctly after WM_read_file -> crash
1897         // do it before for now, but is this correct with multiple windows?
1898         WM_event_add_notifier(C, NC_WINDOW, NULL);
1899
1900         /* load file */
1901         WM_read_file(C, path, op->reports);
1902
1903         G.fileflags &= ~G_FILE_RECOVER;
1904
1905         return OPERATOR_FINISHED;
1906 }
1907
1908 static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1909 {
1910         char filename[FILE_MAX];
1911
1912         wm_autosave_location(filename);
1913         RNA_string_set(op->ptr, "filepath", filename);
1914         WM_event_add_fileselect(C, op);
1915
1916         return OPERATOR_RUNNING_MODAL;
1917 }
1918
1919 static void WM_OT_recover_auto_save(wmOperatorType *ot)
1920 {
1921         ot->name= "Recover Auto Save";
1922         ot->idname= "WM_OT_recover_auto_save";
1923         ot->description="Open an automatically saved file to recover it";
1924         
1925         ot->exec= wm_recover_auto_save_exec;
1926         ot->invoke= wm_recover_auto_save_invoke;
1927         ot->poll= WM_operator_winactive;
1928
1929         WM_operator_properties_filesel(ot, BLENDERFILE, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH, FILE_LONGDISPLAY);
1930 }
1931
1932 /* *************** save file as **************** */
1933
1934 static void untitled(char *name)
1935 {
1936         if(G.save_over == 0 && strlen(name) < FILE_MAX-16) {
1937                 char *c= BLI_last_slash(name);
1938                 
1939                 if(c)
1940                         strcpy(&c[1], "untitled.blend");
1941                 else
1942                         strcpy(name, "untitled.blend");
1943         }
1944 }
1945
1946 static void save_set_compress(wmOperator *op)
1947 {
1948         if(!RNA_struct_property_is_set(op->ptr, "compress")) {
1949                 if(G.save_over) /* keep flag for existing file */
1950                         RNA_boolean_set(op->ptr, "compress", G.fileflags & G_FILE_COMPRESS);
1951                 else /* use userdef for new file */
1952                         RNA_boolean_set(op->ptr, "compress", U.flag & USER_FILECOMPRESS);
1953         }
1954 }
1955
1956 static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1957 {
1958         char name[FILE_MAX];
1959
1960         save_set_compress(op);
1961         
1962         /* if not saved before, get the name of the most recently used .blend file */
1963         if(G.main->name[0]==0 && G.recent_files.first) {
1964                 struct RecentFile *recent = G.recent_files.first;
1965                 BLI_strncpy(name, recent->filepath, FILE_MAX);
1966         }
1967         else
1968                 BLI_strncpy(name, G.main->name, FILE_MAX);
1969         
1970         untitled(name);
1971         RNA_string_set(op->ptr, "filepath", name);
1972         
1973         WM_event_add_fileselect(C, op);
1974
1975         return OPERATOR_RUNNING_MODAL;
1976 }
1977
1978 /* function used for WM_OT_save_mainfile too */
1979 static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
1980 {
1981         char path[FILE_MAX];
1982         int fileflags;
1983         int copy=0;
1984
1985         save_set_compress(op);
1986         
1987         if(RNA_struct_property_is_set(op->ptr, "filepath"))
1988                 RNA_string_get(op->ptr, "filepath", path);
1989         else {
1990                 BLI_strncpy(path, G.main->name, FILE_MAX);
1991                 untitled(path);
1992         }
1993
1994         if(RNA_struct_property_is_set(op->ptr, "copy"))
1995                 copy = RNA_boolean_get(op->ptr, "copy");
1996         
1997         fileflags= G.fileflags;
1998
1999         /* set compression flag */
2000         if(RNA_boolean_get(op->ptr, "compress"))                fileflags |=  G_FILE_COMPRESS;
2001         else                                                                                    fileflags &= ~G_FILE_COMPRESS;
2002         if(RNA_boolean_get(op->ptr, "relative_remap"))  fileflags |=  G_FILE_RELATIVE_REMAP;
2003         else                                                                                    fileflags &= ~G_FILE_RELATIVE_REMAP;
2004 #ifdef USE_BMESH_SAVE_AS_COMPAT
2005         if(RNA_boolean_get(op->ptr, "use_mesh_compat")) fileflags |=  G_FILE_MESH_COMPAT;
2006         else                                                                                    fileflags &= ~G_FILE_MESH_COMPAT;
2007 #endif
2008
2009         if ( WM_write_file(C, path, fileflags, op->reports, copy) != 0)
2010                 return OPERATOR_CANCELLED;
2011
2012         WM_event_add_notifier(C, NC_WM|ND_FILESAVE, NULL);
2013
2014         return OPERATOR_FINISHED;
2015 }
2016
2017 /* function used for WM_OT_save_mainfile too */
2018 static int blend_save_check(bContext *UNUSED(C), wmOperator *op)
2019 {
2020         char filepath[FILE_MAX];
2021         RNA_string_get(op->ptr, "filepath", filepath);
2022         if(!BLO_has_bfile_extension(filepath)) {
2023                 /* some users would prefer BLI_replace_extension(),
2024                  * we keep getting knit-picking bug reports about this - campbell */
2025                 BLI_ensure_extension(filepath, FILE_MAX, ".blend");
2026                 RNA_string_set(op->ptr, "filepath", filepath);
2027                 return TRUE;
2028         }
2029         return FALSE;
2030 }
2031
2032 static void WM_OT_save_as_mainfile(wmOperatorType *ot)
2033 {
2034         ot->name= "Save As Blender File";
2035         ot->idname= "WM_OT_save_as_mainfile";
2036         ot->description="Save the current file in the desired location";
2037         
2038         ot->invoke= wm_save_as_mainfile_invoke;
2039         ot->exec= wm_save_as_mainfile_exec;
2040         ot->check= blend_save_check;
2041         /* ommit window poll so this can work in background mode */
2042
2043         WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY);
2044         RNA_def_boolean(ot->srna, "compress", 0, "Compress", "Write compressed .blend file");
2045         RNA_def_boolean(ot->srna, "relative_remap", 1, "Remap Relative", "Remap relative paths when saving in a different directory");
2046         RNA_def_boolean(ot->srna, "copy", 0, "Save Copy", "Save a copy of the actual working state but does not make saved file active");
2047 #ifdef USE_BMESH_SAVE_AS_COMPAT
2048         RNA_def_boolean(ot->srna, "use_mesh_compat", 0, "Legacy Mesh Format", "Save using legacy mesh format (no ngons)");
2049 #endif
2050 }
2051
2052 /* *************** save file directly ******** */
2053
2054 static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2055 {
2056         char name[FILE_MAX];
2057         int check_existing=1;
2058         int ret;
2059         
2060         /* cancel if no active window */
2061         if (CTX_wm_window(C) == NULL)
2062                 return OPERATOR_CANCELLED;
2063
2064         save_set_compress(op);
2065
2066         /* if not saved before, get the name of the most recently used .blend file */
2067         if(G.main->name[0]==0 && G.recent_files.first) {
2068                 struct RecentFile *recent = G.recent_files.first;
2069                 BLI_strncpy(name, recent->filepath, FILE_MAX);
2070         }
2071         else
2072                 BLI_strncpy(name, G.main->name, FILE_MAX);
2073
2074         untitled(name);
2075         
2076         RNA_string_set(op->ptr, "filepath", name);
2077         
2078         if (RNA_struct_find_property(op->ptr, "check_existing"))
2079                 if (RNA_boolean_get(op->ptr, "check_existing")==0)
2080                         check_existing = 0;
2081         
2082         if (G.save_over) {
2083                 if (check_existing && BLI_exists(name)) {
2084                         uiPupMenuSaveOver(C, op, name);
2085                         ret= OPERATOR_RUNNING_MODAL;
2086                 }
2087                 else {
2088                         ret= wm_save_as_mainfile_exec(C, op);
2089                 }
2090         }
2091         else {
2092                 WM_event_add_fileselect(C, op);
2093                 ret= OPERATOR_RUNNING_MODAL;
2094         }
2095         
2096         return ret;
2097 }
2098
2099 static void WM_OT_save_mainfile(wmOperatorType *ot)
2100 {
2101         ot->name= "Save Blender File";
2102         ot->idname= "WM_OT_save_mainfile";
2103         ot->description="Save the current Blender file";
2104         
2105         ot->invoke= wm_save_mainfile_invoke;
2106         ot->exec= wm_save_as_mainfile_exec;
2107         ot->check= blend_save_check;
2108         /* ommit window poll so this can work in background mode */
2109         
2110         WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY);
2111         RNA_def_boolean(ot->srna, "compress", 0, "Compress", "Write compressed .blend file");
2112         RNA_def_boolean(ot->srna, "relative_remap", 0, "Remap Relative", "Remap relative paths when saving in a different directory");
2113 }
2114
2115 /* XXX: move these collada operators to a more appropriate place */
2116 #ifdef WITH_COLLADA
2117
2118 #include "../../collada/collada.h"
2119
2120 static int wm_collada_export_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2121 {       
2122         if(!RNA_struct_property_is_set(op->ptr, "filepath")) {
2123                 char filepath[FILE_MAX];
2124                 BLI_strncpy(filepath, G.main->name, sizeof(filepath));
2125                 BLI_replace_extension(filepath, sizeof(filepath), ".dae");
2126                 RNA_string_set(op->ptr, "filepath", filepath);
2127         }
2128
2129         WM_event_add_fileselect(C, op);
2130
2131         return OPERATOR_RUNNING_MODAL;
2132 }
2133
2134 /* function used for WM_OT_save_mainfile too */
2135 static int wm_collada_export_exec(bContext *C, wmOperator *op)
2136 {
2137         char filename[FILE_MAX];
2138         int selected;
2139         
2140         if(!RNA_struct_property_is_set(op->ptr, "filepath")) {
2141                 BKE_report(op->reports, RPT_ERROR, "No filename given");
2142                 return OPERATOR_CANCELLED;
2143         }
2144
2145         RNA_string_get(op->ptr, "filepath", filename);
2146         selected = RNA_boolean_get(op->ptr, "selected");
2147         if(collada_export(CTX_data_scene(C), filename, selected)) {
2148                 return OPERATOR_FINISHED;
2149         }
2150         else {
2151                 return OPERATOR_CANCELLED;
2152         }
2153 }
2154
2155 static void WM_OT_collada_export(wmOperatorType *ot)
2156 {
2157         ot->name= "Export COLLADA";
2158         ot->idname= "WM_OT_collada_export";
2159         
2160         ot->invoke= wm_collada_export_invoke;
2161         ot->exec= wm_collada_export_exec;
2162         ot->poll= WM_operator_winactive;
2163         
2164         WM_operator_properties_filesel(ot, FOLDERFILE|COLLADAFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY);
2165         RNA_def_boolean(ot->srna, "selected", 0, "Export only selected",
2166                 "Export only selected elements");
2167 }
2168
2169 /* function used for WM_OT_save_mainfile too */
2170 static int wm_collada_import_exec(bContext *C, wmOperator *op)
2171 {
2172         char filename[FILE_MAX];
2173         
2174         if(!RNA_struct_property_is_set(op->ptr, "filepath")) {
2175                 BKE_report(op->reports, RPT_ERROR, "No filename given");
2176                 return OPERATOR_CANCELLED;
2177         }
2178
2179         RNA_string_get(op->ptr, "filepath", filename);
2180         if(collada_import(C, filename)) return OPERATOR_FINISHED;
2181         
2182         BKE_report(op->reports, RPT_ERROR, "Errors found during parsing COLLADA document. Please see console for error log.");
2183         
2184         return OPERATOR_FINISHED;
2185 }
2186
2187 static void WM_OT_collada_import(wmOperatorType *ot)
2188 {
2189         ot->name= "Import COLLADA";
2190         ot->idname= "WM_OT_collada_import";
2191         
2192         ot->invoke= WM_operator_filesel;
2193         ot->exec= wm_collada_import_exec;
2194         ot->poll= WM_operator_winactive;
2195         
2196         WM_operator_properties_filesel(ot, FOLDERFILE|COLLADAFILE, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY);
2197 }
2198
2199 #endif
2200
2201
2202 /* *********************** */
2203
2204 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
2205 {
2206         ot->name= "Toggle Fullscreen";
2207         ot->idname= "WM_OT_window_fullscreen_toggle";
2208         ot->description="Toggle the current window fullscreen";
2209
2210         ot->exec= wm_window_fullscreen_toggle_exec;
2211         ot->poll= WM_operator_winactive;
2212 }
2213
2214 static int wm_exit_blender_op(bContext *C, wmOperator *op)
2215 {
2216         WM_operator_free(op);
2217         
2218         WM_exit(C);     
2219         
2220         return OPERATOR_FINISHED;
2221 }
2222
2223 static void WM_OT_quit_blender(wmOperatorType *ot)
2224 {
2225         ot->name= "Quit Blender";
2226         ot->idname= "WM_OT_quit_blender";
2227         ot->description= "Quit Blender";
2228
2229         ot->invoke= WM_operator_confirm;
2230         ot->exec= wm_exit_blender_op;
2231         ot->poll= WM_operator_winactive;
2232 }
2233
2234 /* *********************** */
2235
2236 #if defined(WIN32)
2237
2238 static int wm_console_toggle_op(bContext *UNUSED(C), wmOperator *UNUSED(op))
2239 {
2240         GHOST_toggleConsole(2);
2241         return OPERATOR_FINISHED;
2242 }
2243
2244 static void WM_OT_console_toggle(wmOperatorType *ot)
2245 {
2246         ot->name= "Toggle System Console";
2247         ot->idname= "WM_OT_console_toggle";
2248         ot->description= "Toggle System Console";
2249         
2250         ot->exec= wm_console_toggle_op;
2251         ot->poll= WM_operator_winactive;
2252 }
2253
2254 #endif
2255
2256 /* ************ default paint cursors, draw always around cursor *********** */
2257 /*
2258  - returns handler to free 
2259  - poll(bContext): returns 1 if draw should happen
2260  - draw(bContext): drawing callback for paint cursor
2261 */
2262
2263 void *WM_paint_cursor_activate(wmWindowManager *wm, int (*poll)(bContext *C),
2264                                    wmPaintCursorDraw draw, void *customdata)
2265 {
2266         wmPaintCursor *pc= MEM_callocN(sizeof(wmPaintCursor), "paint cursor");
2267         
2268         BLI_addtail(&wm->paintcursors, pc);
2269         
2270         pc->customdata = customdata;
2271         pc->poll= poll;
2272         pc->draw= draw;
2273         
2274         return pc;
2275 }
2276
2277 void WM_paint_cursor_end(wmWindowManager *wm, void *handle)
2278 {
2279         wmPaintCursor *pc;
2280         
2281         for(pc= wm->paintcursors.first; pc; pc= pc->next) {
2282                 if(pc == (wmPaintCursor *)handle) {
2283                         BLI_remlink(&wm->paintcursors, pc);
2284                         MEM_freeN(pc);
2285                         return;
2286                 }
2287         }
2288 }
2289
2290 /* ************ window gesture operator-callback definitions ************** */
2291 /*
2292  * These are default callbacks for use in operators requiring gesture input
2293  */
2294
2295 /* **************** Border gesture *************** */
2296
2297 /* Border gesture has two types:
2298    1) WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border 
2299    2) WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends
2300
2301    It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type)
2302 */
2303
2304 static int border_apply_rect(wmOperator *op)
2305 {
2306         wmGesture *gesture= op->customdata;
2307         rcti *rect= gesture->customdata;
2308         
2309         if(rect->xmin==rect->xmax || rect->ymin==rect->ymax)
2310                 return 0;
2311
2312         
2313         /* operator arguments and storage. */
2314         RNA_int_set(op->ptr, "xmin", MIN2(rect->xmin, rect->xmax) );
2315         RNA_int_set(op->ptr, "ymin", MIN2(rect->ymin, rect->ymax) );
2316         RNA_int_set(op->ptr, "xmax", MAX2(rect->xmin, rect->xmax) );
2317         RNA_int_set(op->ptr, "ymax", MAX2(rect->ymin, rect->ymax) );
2318
2319         return 1;
2320 }
2321
2322 static int border_apply(bContext *C, wmOperator *op, int gesture_mode)
2323 {
2324         if (!border_apply_rect(op))
2325                 return 0;
2326         
2327         /* XXX weak; border should be configured for this without reading event types */
2328         if( RNA_struct_find_property(op->ptr, "gesture_mode") )
2329                 RNA_int_set(op->ptr, "gesture_mode", gesture_mode);
2330
2331         op->type->exec(C, op);
2332         return 1;
2333 }
2334
2335 static void wm_gesture_end(bContext *C, wmOperator *op)
2336 {
2337         wmGesture *gesture= op->customdata;
2338         
2339         WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
2340         op->customdata= NULL;
2341
2342         ED_area_tag_redraw(CTX_wm_area(C));
2343         
2344         if( RNA_struct_find_property(op->ptr, "cursor") )
2345                 WM_cursor_restore(CTX_wm_window(C));
2346 }
2347
2348 int WM_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
2349 {
2350         if(ISTWEAK(event->type))
2351                 op->customdata= WM_gesture_new(C, event, WM_GESTURE_RECT);
2352         else
2353                 op->customdata= WM_gesture_new(C, event, WM_GESTURE_CROSS_RECT);
2354
2355         /* add modal handler */
2356         WM_event_add_modal_handler(C, op);
2357         
2358         wm_gesture_tag_redraw(C);
2359
2360         return OPERATOR_RUNNING_MODAL;
2361 }
2362
2363 int WM_border_select_modal(bContext *C, wmOperator *op, wmEvent *event)
2364 {
2365         wmGesture *gesture= op->customdata;
2366         rcti *rect= gesture->customdata;
2367         int sx, sy;
2368         
2369         if(event->type== MOUSEMOVE) {
2370                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2371
2372                 if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
2373                         rect->xmin= rect->xmax= event->x - sx;
2374                         rect->ymin= rect->ymax= event->y - sy;
2375                 }
2376                 else {
2377                         rect->xmax= event->x - sx;
2378                         rect->ymax= event->y - sy;
2379                 }
2380                 border_apply_rect(op);
2381
2382                 wm_gesture_tag_redraw(C);
2383         }
2384         else if (event->type==EVT_MODAL_MAP) {
2385                 switch (event->val) {
2386                 case GESTURE_MODAL_BEGIN:
2387                         if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
2388                                 gesture->mode= 1;
2389                                 wm_gesture_tag_redraw(C);
2390                         }
2391                         break;
2392                 case GESTURE_MODAL_SELECT:
2393                 case GESTURE_MODAL_DESELECT:
2394                 case GESTURE_MODAL_IN:
2395                 case GESTURE_MODAL_OUT:
2396                         if(border_apply(C, op, event->val)) {
2397                                 wm_gesture_end(C, op);
2398                                 return OPERATOR_FINISHED;
2399                         }
2400                         wm_gesture_end(C, op);
2401                         return OPERATOR_CANCELLED;
2402                         break;
2403
2404                 case GESTURE_MODAL_CANCEL:
2405                         wm_gesture_end(C, op);
2406                         return OPERATOR_CANCELLED;
2407                 }
2408
2409         }
2410 //      // Allow view navigation???
2411 //      else {
2412 //              return OPERATOR_PASS_THROUGH;
2413 //      }
2414
2415         return OPERATOR_RUNNING_MODAL;
2416 }
2417
2418 int WM_border_select_cancel(bContext *C, wmOperator *op)
2419 {
2420         wm_gesture_end(C, op);
2421
2422         return OPERATOR_CANCELLED;
2423 }
2424
2425 /* **************** circle gesture *************** */
2426 /* works now only for selection or modal paint stuff, calls exec while hold mouse, exit on release */
2427
2428 #ifdef GESTURE_MEMORY
2429 int circle_select_size= 25; // XXX - need some operator memory thing\!
2430 #endif
2431
2432 int WM_gesture_circle_invoke(bContext *C, wmOperator *op, wmEvent *event)
2433 {
2434         op->customdata= WM_gesture_new(C, event, WM_GESTURE_CIRCLE);
2435         
2436         /* add modal handler */
2437         WM_event_add_modal_handler(C, op);
2438         
2439         wm_gesture_tag_redraw(C);
2440         
2441         return OPERATOR_RUNNING_MODAL;
2442 }
2443
2444 static void gesture_circle_apply(bContext *C, wmOperator *op)
2445 {
2446         wmGesture *gesture= op->customdata;
2447         rcti *rect= gesture->customdata;
2448         
2449         if(RNA_int_get(op->ptr, "gesture_mode")==GESTURE_MODAL_NOP)
2450                 return;
2451
2452         /* operator arguments and storage. */
2453         RNA_int_set(op->ptr, "x", rect->xmin);
2454         RNA_int_set(op->ptr, "y", rect->ymin);
2455         RNA_int_set(op->ptr, "radius", rect->xmax);
2456         
2457         if(op->type->exec)
2458                 op->type->exec(C, op);
2459 #ifdef GESTURE_MEMORY
2460         circle_select_size= rect->xmax;
2461 #endif
2462 }
2463
2464 int WM_gesture_circle_modal(bContext *C, wmOperator *op, wmEvent *event)
2465 {
2466         wmGesture *gesture= op->customdata;
2467         rcti *rect= gesture->customdata;
2468         int sx, sy;
2469
2470         if(event->type== MOUSEMOVE) {
2471                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2472
2473                 rect->xmin= event->x - sx;
2474                 rect->ymin= event->y - sy;
2475
2476                 wm_gesture_tag_redraw(C);
2477
2478                 if(gesture->mode)
2479                         gesture_circle_apply(C, op);
2480         }
2481         else if (event->type==EVT_MODAL_MAP) {
2482                 switch (event->val) {
2483                 case GESTURE_MODAL_CIRCLE_ADD:
2484                         rect->xmax += 2 + rect->xmax/10;
2485                         wm_gesture_tag_redraw(C);
2486                         break;
2487                 case GESTURE_MODAL_CIRCLE_SUB:
2488                         rect->xmax -= 2 + rect->xmax/10;
2489                         if(rect->xmax < 1) rect->xmax= 1;
2490                         wm_gesture_tag_redraw(C);
2491                         break;
2492                 case GESTURE_MODAL_SELECT:
2493                 case GESTURE_MODAL_DESELECT:
2494                 case GESTURE_MODAL_NOP:
2495                         if(RNA_struct_find_property(op->ptr, "gesture_mode"))
2496                                 RNA_int_set(op->ptr, "gesture_mode", event->val);
2497
2498                         if(event->val != GESTURE_MODAL_NOP) {
2499                                 /* apply first click */
2500                                 gesture_circle_apply(C, op);
2501                                 gesture->mode= 1;
2502                                 wm_gesture_tag_redraw(C);
2503                         }
2504                         break;
2505
2506                 case GESTURE_MODAL_CANCEL:
2507                 case GESTURE_MODAL_CONFIRM:
2508                         wm_gesture_end(C, op);
2509                         return OPERATOR_FINISHED; /* use finish or we dont get an undo */
2510                 }
2511         }
2512 //      // Allow view navigation???
2513 //      else {
2514 //              return OPERATOR_PASS_THROUGH;
2515 //      }
2516
2517         return OPERATOR_RUNNING_MODAL;
2518 }
2519
2520 int WM_gesture_circle_cancel(bContext *C, wmOperator *op)
2521 {
2522         wm_gesture_end(C, op);
2523
2524         return OPERATOR_CANCELLED;
2525 }
2526
2527 #if 0
2528 /* template to copy from */
2529 void WM_OT_circle_gesture(wmOperatorType *ot)
2530 {
2531         ot->name= "Circle Gesture";
2532         ot->idname= "WM_OT_circle_gesture";
2533         ot->description="Enter rotate mode with a circular gesture";
2534         
2535         ot->invoke= WM_gesture_circle_invoke;
2536         ot->modal= WM_gesture_circle_modal;
2537         
2538         ot->poll= WM_operator_winactive;
2539         
2540         RNA_def_property(ot->srna, "x", PROP_INT, PROP_NONE);
2541         RNA_def_property(ot->srna, "y", PROP_INT, PROP_NONE);
2542         RNA_def_property(ot->srna, "radius", PROP_INT, PROP_NONE);
2543
2544 }
2545 #endif
2546
2547 /* **************** Tweak gesture *************** */
2548
2549 static void tweak_gesture_modal(bContext *C, wmEvent *event)
2550 {
2551         wmWindow *window= CTX_wm_window(C);
2552         wmGesture *gesture= window->tweak;
2553         rcti *rect= gesture->customdata;
2554         int sx, sy, val;
2555         
2556         switch(event->type) {
2557                 case MOUSEMOVE:
2558                 case INBETWEEN_MOUSEMOVE:
2559                         
2560                         wm_subwindow_getorigin(window, gesture->swinid, &sx, &sy);
2561                         
2562                         rect->xmax= event->x - sx;
2563                         rect->ymax= event->y - sy;
2564                         
2565                         if((val= wm_gesture_evaluate(gesture))) {
2566                                 wmEvent tevent;
2567
2568                                 tevent= *(window->eventstate);
2569                                 if(gesture->event_type==LEFTMOUSE)
2570                                         tevent.type= EVT_TWEAK_L;
2571                                 else if(gesture->event_type==RIGHTMOUSE)
2572                                         tevent.type= EVT_TWEAK_R;
2573                                 else
2574                                         tevent.type= EVT_TWEAK_M;
2575                                 tevent.val= val;
2576                                 /* mouse coords! */
2577                                 wm_event_add(window, &tevent);
2578                                 
2579                                 WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
2580                         }
2581                         
2582                         break;
2583                         
2584                 case LEFTMOUSE:
2585                 case RIGHTMOUSE:
2586                 case MIDDLEMOUSE:
2587                         if(gesture->event_type==event->type) {
2588                                 WM_gesture_end(C, gesture);
2589
2590                                 /* when tweak fails we should give the other keymap entries a chance */
2591                                 event->val= KM_RELEASE;
2592                         }
2593                         break;
2594                 default:
2595                         if(!ISTIMER(event->type)) {
2596                                 WM_gesture_end(C, gesture);
2597                         }
2598                         break;
2599         }
2600 }
2601
2602 /* standard tweak, called after window handlers passed on event */
2603 void wm_tweakevent_test(bContext *C, wmEvent *event, int action)
2604 {
2605         wmWindow *win= CTX_wm_window(C);
2606         
2607         if(win->tweak==NULL) {
2608                 if(CTX_wm_region(C)) {
2609                         if(event->val==KM_PRESS) { 
2610                                 if( ELEM3(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) )
2611                                         win->tweak= WM_gesture_new(C, event, WM_GESTURE_TWEAK);
2612                         }
2613                 }
2614         }
2615         else {
2616                 /* no tweaks if event was handled */
2617                 if((action & WM_HANDLER_BREAK)) {
2618                         WM_gesture_end(C, win->tweak);
2619                 }
2620                 else
2621                         tweak_gesture_modal(C, event);
2622         }
2623 }
2624
2625 /* *********************** lasso gesture ****************** */
2626
2627 int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, wmEvent *event)
2628 {
2629         op->customdata= WM_gesture_new(C, event, WM_GESTURE_LASSO);
2630         
2631         /* add modal handler */
2632         WM_event_add_modal_handler(C, op);
2633         
2634         wm_gesture_tag_redraw(C);
2635         
2636         if( RNA_struct_find_property(op->ptr, "cursor") )
2637                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2638         
2639         return OPERATOR_RUNNING_MODAL;
2640 }
2641
2642 int WM_gesture_lines_invoke(bContext *C, wmOperator *op, wmEvent *event)
2643 {
2644         op->customdata= WM_gesture_new(C, event, WM_GESTURE_LINES);
2645         
2646         /* add modal handler */
2647         WM_event_add_modal_handler(C, op);
2648         
2649         wm_gesture_tag_redraw(C);
2650         
2651         if( RNA_struct_find_property(op->ptr, "cursor") )
2652                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2653         
2654         return OPERATOR_RUNNING_MODAL;
2655 }
2656
2657
2658 static void gesture_lasso_apply(bContext *C, wmOperator *op)
2659 {
2660         wmGesture *gesture= op->customdata;
2661         PointerRNA itemptr;
2662         float loc[2];
2663         int i;
2664         short *lasso= gesture->customdata;
2665         
2666         /* operator storage as path. */
2667
2668         for(i=0; i<gesture->points; i++, lasso+=2) {
2669                 loc[0]= lasso[0];
2670                 loc[1]= lasso[1];
2671                 RNA_collection_add(op->ptr, "path", &itemptr);
2672                 RNA_float_set_array(&itemptr, "loc", loc);
2673         }
2674         
2675         wm_gesture_end(C, op);
2676                 
2677         if(op->type->exec)
2678                 op->type->exec(C, op);
2679 }
2680
2681 int WM_gesture_lasso_modal(bContext *C, wmOperator *op, wmEvent *event)
2682 {
2683         wmGesture *gesture= op->customdata;
2684         int sx, sy;
2685         
2686         switch(event->type) {
2687                 case MOUSEMOVE:
2688                 case INBETWEEN_MOUSEMOVE:
2689                         
2690                         wm_gesture_tag_redraw(C);
2691                         
2692                         wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2693
2694                         if(gesture->points == gesture->size) {
2695                                 short *old_lasso = gesture->customdata;
2696                                 gesture->customdata= MEM_callocN(2*sizeof(short)*(gesture->size + WM_LASSO_MIN_POINTS), "lasso points");
2697                                 memcpy(gesture->customdata, old_lasso, 2*sizeof(short)*gesture->size);
2698                                 gesture->size = gesture->size + WM_LASSO_MIN_POINTS;
2699                                 MEM_freeN(old_lasso);
2700                                 // printf("realloc\n");
2701                         }
2702
2703                         {
2704                                 int x, y;
2705                                 short *lasso= gesture->customdata;
2706                                 
2707                                 lasso += (2 * gesture->points - 2);
2708                                 x = (event->x - sx - lasso[0]);
2709                                 y = (event->y - sy - lasso[1]);
2710                                 
2711                                 /* make a simple distance check to get a smoother lasso
2712                                    add only when at least 2 pixels between this and previous location */
2713                                 if((x*x+y*y) > 4) {
2714                                         lasso += 2;
2715                                         lasso[0] = event->x - sx;
2716                                         lasso[1] = event->y - sy;
2717                                         gesture->points++;
2718                                 }
2719                         }
2720                         break;
2721                         
2722                 case LEFTMOUSE:
2723                 case MIDDLEMOUSE:
2724                 case RIGHTMOUSE:
2725                         if(event->val==KM_RELEASE) {    /* key release */
2726                                 gesture_lasso_apply(C, op);
2727                                 return OPERATOR_FINISHED;
2728                         }
2729                         break;
2730                 case ESCKEY:
2731                         wm_gesture_end(C, op);
2732                         return OPERATOR_CANCELLED;
2733         }
2734         return OPERATOR_RUNNING_MODAL;
2735 }
2736
2737 int WM_gesture_lines_modal(bContext *C, wmOperator *op, wmEvent *event)
2738 {
2739         return WM_gesture_lasso_modal(C, op, event);
2740 }
2741
2742 int WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
2743 {
2744         wm_gesture_end(C, op);
2745
2746         return OPERATOR_CANCELLED;
2747 }
2748
2749 int WM_gesture_lines_cancel(bContext *C, wmOperator *op)
2750 {
2751         wm_gesture_end(C, op);
2752
2753         return OPERATOR_CANCELLED;
2754 }
2755
2756 #if 0
2757 /* template to copy from */
2758
2759 static int gesture_lasso_exec(bContext *C, wmOperator *op)
2760 {
2761         RNA_BEGIN(op->ptr, itemptr, "path") {
2762                 float loc[2];
2763                 
2764                 RNA_float_get_array(&itemptr, "loc", loc);
2765                 printf("Location: %f %f\n", loc[0], loc[1]);
2766         }
2767         RNA_END;
2768         
2769         return OPERATOR_FINISHED;
2770 }
2771
2772 void WM_OT_lasso_gesture(wmOperatorType *ot)
2773 {
2774         PropertyRNA *prop;
2775         
2776         ot->name= "Lasso Gesture";
2777         ot->idname= "WM_OT_lasso_gesture";
2778         ot->description="Select objects within the lasso as you move the pointer";
2779         
2780         ot->invoke= WM_gesture_lasso_invoke;
2781         ot->modal= WM_gesture_lasso_modal;
2782         ot->exec= gesture_lasso_exec;
2783         
2784         ot->poll= WM_operator_winactive;
2785         
2786         prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
2787         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
2788 }
2789 #endif
2790
2791 /* *********************** straight line gesture ****************** */
2792
2793 static int straightline_apply(bContext *C, wmOperator *op)
2794 {
2795         wmGesture *gesture= op->customdata;
2796         rcti *rect= gesture->customdata;
2797         
2798         if(rect->xmin==rect->xmax && rect->ymin==rect->ymax)
2799                 return 0;
2800         
2801         /* operator arguments and storage. */
2802         RNA_int_set(op->ptr, "xstart", rect->xmin);
2803         RNA_int_set(op->ptr, "ystart", rect->ymin);
2804         RNA_int_set(op->ptr, "xend", rect->xmax);
2805         RNA_int_set(op->ptr, "yend", rect->ymax);
2806
2807         if(op->type->exec)
2808                 op->type->exec(C, op);
2809         
2810         return 1;
2811 }
2812
2813
2814 int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, wmEvent *event)
2815 {
2816         op->customdata= WM_gesture_new(C, event, WM_GESTURE_STRAIGHTLINE);
2817         
2818         /* add modal handler */
2819         WM_event_add_modal_handler(C, op);
2820         
2821         wm_gesture_tag_redraw(C);
2822         
2823         if( RNA_struct_find_property(op->ptr, "cursor") )
2824                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2825                 
2826         return OPERATOR_RUNNING_MODAL;
2827 }
2828
2829 int WM_gesture_straightline_modal(bContext *C, wmOperator *op, wmEvent *event)
2830 {
2831         wmGesture *gesture= op->customdata;
2832         rcti *rect= gesture->customdata;
2833         int sx, sy;
2834         
2835         if(event->type== MOUSEMOVE) {
2836                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2837                 
2838                 if(gesture->mode==0) {
2839                         rect->xmin= rect->xmax= event->x - sx;
2840                         rect->ymin= rect->ymax= event->y - sy;
2841                 }
2842                 else {
2843                         rect->xmax= event->x - sx;
2844                         rect->ymax= event->y - sy;
2845                         straightline_apply(C, op);
2846                 }
2847                 
2848                 wm_gesture_tag_redraw(C);
2849         }
2850         else if (event->type==EVT_MODAL_MAP) {
2851                 switch (event->val) {
2852                         case GESTURE_MODAL_BEGIN:
2853                                 if(gesture->mode==0) {
2854                                         gesture->mode= 1;
2855                                         wm_gesture_tag_redraw(C);
2856                                 }
2857                                 break;
2858                         case GESTURE_MODAL_SELECT:
2859                                 if(straightline_apply(C, op)) {
2860                                         wm_gesture_end(C, op);
2861                                         return OPERATOR_FINISHED;
2862                                 }
2863                                 wm_gesture_end(C, op);
2864                                 return OPERATOR_CANCELLED;
2865                                 break;
2866                                 
2867                         case GESTURE_MODAL_CANCEL:
2868                                 wm_gesture_end(C, op);
2869                                 return OPERATOR_CANCELLED;
2870                 }
2871                 
2872         }
2873
2874         return OPERATOR_RUNNING_MODAL;
2875 }
2876
2877 int WM_gesture_straightline_cancel(bContext *C, wmOperator *op)
2878 {
2879         wm_gesture_end(C, op);
2880
2881         return OPERATOR_CANCELLED;
2882 }
2883
2884 #if 0
2885 /* template to copy from */
2886 void WM_OT_straightline_gesture(wmOperatorType *ot)
2887 {
2888         PropertyRNA *prop;
2889         
2890         ot->name= "Straight Line Gesture";
2891         ot->idname= "WM_OT_straightline_gesture";
2892         ot->description="Draw a straight line as you move the pointer";
2893         
2894         ot->invoke= WM_gesture_straightline_invoke;
2895         ot->modal= WM_gesture_straightline_modal;
2896         ot->exec= gesture_straightline_exec;
2897         
2898         ot->poll= WM_operator_winactive;
2899         
2900         WM_operator_properties_gesture_straightline(ot, 0);
2901 }
2902 #endif
2903
2904 /* *********************** radial control ****************** */
2905
2906 static const int WM_RADIAL_CONTROL_DISPLAY_SIZE = 200;
2907
2908 typedef struct {
2909         PropertyType type;
2910         PropertySubType subtype;
2911         PointerRNA ptr, col_ptr, fill_col_ptr, rot_ptr, zoom_ptr, image_id_ptr;
2912         PropertyRNA *prop, *col_prop, *fill_col_prop, *rot_prop, *zoom_prop;
2913         StructRNA *image_id_srna;
2914         float initial_value, current_value, min_value, max_value;
2915         int initial_mouse[2];
2916         unsigned int gltex;
2917         ListBase orig_paintcursors;
2918         void *cursor;
2919 } RadialControl;
2920
2921 static void radial_control_set_initial_mouse(RadialControl *rc, wmEvent *event)
2922 {
2923         float d[2] = {0, 0};
2924         float zoom[2] = {1, 1};
2925
2926         rc->initial_mouse[0]= event->x;
2927         rc->initial_mouse[1]= event->y;
2928
2929         switch(rc->subtype) {
2930         case PROP_DISTANCE:
2931                 d[0] = rc->initial_value;
2932                 break;
2933         case PROP_FACTOR:
2934                 d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * (1 - rc->initial_value);
2935                 break;
2936         case PROP_ANGLE:
2937                 d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * cos(rc->initial_value);
2938                 d[1] = WM_RADIAL_CONTROL_DISPLAY_SIZE * sin(rc->initial_value);
2939                 break;
2940         default:
2941                 return;
2942         }
2943
2944         if(rc->zoom_prop) {
2945                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2946                 d[0] *= zoom[0];
2947                 d[1] *= zoom[1];
2948         }
2949
2950         rc->initial_mouse[0]-= d[0];
2951         rc->initial_mouse[1]-= d[1];
2952 }
2953
2954 static void radial_control_set_tex(RadialControl *rc)
2955 {
2956         ImBuf *ibuf;
2957
2958         switch(RNA_type_to_ID_code(rc->image_id_ptr.type)) {
2959         case ID_BR:
2960                 if((ibuf = brush_gen_radial_control_imbuf(rc->image_id_ptr.data))) {
2961                         glGenTextures(1, &rc->gltex);
2962                         glBindTexture(GL_TEXTURE_2D, rc->gltex);
2963                         glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, ibuf->x, ibuf->y, 0,
2964                                      GL_ALPHA, GL_FLOAT, ibuf->rect_float);
2965                         MEM_freeN(ibuf->rect_float);
2966                         MEM_freeN(ibuf);
2967                 }
2968                 break;
2969         default:
2970                 break;
2971         }
2972 }
2973
2974 static void radial_control_paint_tex(RadialControl *rc, float radius, float alpha)
2975 {
2976         float col[3] = {0, 0, 0};
2977         float rot;
2978
2979         /* set fill color */
2980         if(rc->fill_col_prop)
2981                 RNA_property_float_get_array(&rc->fill_col_ptr, rc->fill_col_prop, col);
2982         glColor4f(col[0], col[1], col[2], alpha);
2983
2984         if(rc->gltex) {
2985                 glBindTexture(GL_TEXTURE_2D, rc->gltex);
2986
2987                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2988                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2989
2990                 /* set up rotation if available */
2991                 if(rc->rot_prop) {
2992                         rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop);
2993                         glPushMatrix();
2994                         glRotatef(RAD2DEGF(rot), 0, 0, 1);
2995                 }
2996
2997                 /* draw textured quad */
2998                 glEnable(GL_TEXTURE_2D);
2999                 glBegin(GL_QUADS);
3000                 glTexCoord2f(0,0);
3001                 glVertex2f(-radius, -radius);
3002                 glTexCoord2f(1,0);
3003                 glVertex2f(radius, -radius);
3004                 glTexCoord2f(1,1);
3005                 glVertex2f(radius, radius);
3006                 glTexCoord2f(0,1);
3007                 glVertex2f(-radius, radius);
3008                 glEnd();
3009                 glDisable(GL_TEXTURE_2D);
3010
3011                 /* undo rotation */
3012                 if(rc->rot_prop)
3013                         glPopMatrix();
3014         }
3015         else {
3016                 /* flat color if no texture available */
3017                 glutil_draw_filled_arc(0, M_PI * 2, radius, 40);
3018         }
3019 }
3020
3021 static void radial_control_paint_cursor(bContext *C, int x, int y, void *customdata)
3022 {
3023         RadialControl *rc = customdata;
3024         ARegion *ar = CTX_wm_region(C);
3025         float r1=0.0f, r2=0.0f, tex_radius, alpha;
3026         float zoom[2], col[3] = {1, 1, 1};
3027
3028         switch(rc->subtype) {
3029         case PROP_DISTANCE:
3030                 r1= rc->current_value;
3031                 r2= rc->initial_value;
3032                 tex_radius= r1;
3033                 alpha = 0.75;
3034                 break;
3035         case PROP_FACTOR:
3036                 r1= (1 - rc->current_value) * WM_RADIAL_CONTROL_DISPLAY_SIZE;
3037                 r2= tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE;
3038                 alpha = rc->current_value / 2.0f + 0.5f;
3039                 break;
3040         case PROP_ANGLE:
3041                 r1= r2= tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE;
3042                 alpha = 0.75;
3043                 break;
3044         default:
3045                 tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE; /* note, this is a dummy value */
3046                 alpha = 0.75;
3047                 break;
3048         }
3049
3050         /* Keep cursor in the original place */
3051         x = rc->initial_mouse[0] - ar->winrct.xmin;
3052         y = rc->initial_mouse[1] - ar->winrct.ymin;
3053         glTranslatef((float)x, (float)y, 0.0f);
3054
3055         glEnable(GL_BLEND);
3056         glEnable(GL_LINE_SMOOTH);
3057
3058         /* apply zoom if available */
3059         if(rc->zoom_prop) {
3060                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
3061                 glScalef(zoom[0], zoom[1], 1);
3062         }
3063
3064         /* draw rotated texture */
3065         radial_control_paint_tex(rc, tex_radius, alpha);
3066
3067         /* set line color */
3068         if(rc->col_prop)
3069                 RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col);
3070         glColor4f(col[0], col[1], col[2], 0.5);
3071
3072         if(rc->subtype == PROP_ANGLE) {
3073                 glPushMatrix();
3074                 /* draw original angle line */
3075                 glRotatef(RAD2DEGF(rc->initial_value), 0, 0, 1);
3076                 fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
3077                 /* draw new angle line */
3078                 glRotatef(RAD2DEGF(rc->current_value - rc->initial_value), 0, 0, 1);
3079                 fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
3080                 glPopMatrix();
3081         }
3082
3083         /* draw circles on top */
3084         glutil_draw_lined_arc(0.0, (float)(M_PI*2.0), r1, 40);
3085         glutil_draw_lined_arc(0.0, (float)(M_PI*2.0), r2, 40);
3086
3087         glDisable(GL_BLEND);
3088         glDisable(GL_LINE_SMOOTH);
3089 }
3090
3091 typedef enum {
3092         RC_PROP_ALLOW_MISSING = 1,
3093         RC_PROP_REQUIRE_FLOAT = 2,
3094         RC_PROP_REQUIRE_BOOL = 4,
3095 } RCPropFlags;
3096
3097 /* attempt to retrieve the rna pointer/property from an rna path;
3098    returns 0 for failure, 1 for success, and also 1 if property is not
3099    set */
3100 static int radial_control_get_path(PointerRNA *ctx_ptr, wmOperator *op,
3101                                    const char *name, PointerRNA *r_ptr,
3102                                    PropertyRNA **r_prop, int req_length, RCPropFlags flags)
3103 {
3104         PropertyRNA *unused_prop;
3105         int len;
3106         char *str;
3107
3108         /* check flags */
3109         if((flags & RC_PROP_REQUIRE_BOOL) && (flags & RC_PROP_REQUIRE_FLOAT)) {
3110                 BKE_reportf(op->reports, RPT_ERROR, "Property can't be both boolean and float");
3111                 return 0;
3112         }
3113
3114         /* get an rna string path from the operator's properties */
3115         if(!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0)))
3116                 return 1;
3117
3118         if(str[0] == '\0') {
3119                 if(r_prop) *r_prop = NULL;
3120                 MEM_freeN(str);
3121                 return 1;
3122         }
3123
3124         if(!r_prop)
3125                 r_prop = &unused_prop;
3126
3127         /* get rna from path */
3128         if(!RNA_path_resolve(ctx_ptr, str, r_ptr, r_prop)) {
3129                 MEM_freeN(str);
3130                 if(flags & RC_PROP_ALLOW_MISSING)
3131                         return 1;
3132                 else {
3133                         BKE_reportf(op->reports, RPT_ERROR, "Couldn't resolve path %s", name);
3134                         return 0;
3135                 }
3136         }
3137
3138         /* check property type */
3139         if(flags & (RC_PROP_REQUIRE_BOOL | RC_PROP_REQUIRE_FLOAT)) {
3140                 PropertyType prop_type = RNA_property_type(*r_prop);
3141
3142                 if(((flags & RC_PROP_REQUIRE_BOOL) && (prop_type != PROP_BOOLEAN)) ||
3143                    ((flags & RC_PROP_REQUIRE_FLOAT) && prop_type != PROP_FLOAT)) {
3144                         MEM_freeN(str);
3145                         BKE_reportf(op->reports, RPT_ERROR,
3146                                     "Property from path %s is not a float", name);
3147                         return 0;
3148                 }
3149         }
3150         
3151         /* check property's array length */
3152         if(*r_prop && (len = RNA_property_array_length(r_ptr, *r_prop)) != req_length) {
3153                 MEM_freeN(str);
3154                 BKE_reportf(op->reports, RPT_ERROR,
3155                             "Property from path %s has length %d instead of %d",
3156                             name, len, req_length);
3157                 return 0;
3158         }
3159
3160         /* success */
3161         MEM_freeN(str);
3162         return 1;
3163 }
3164
3165 /* initialize the rna pointers and properties using rna paths */
3166 static int radial_control_get_properties(bContext *C, wmOperator *op)
3167 {
3168         RadialControl *rc = op->customdata;
3169         PointerRNA ctx_ptr, use_secondary_ptr;
3170         PropertyRNA *use_secondary_prop;
3171         const char *data_path;
3172
3173         RNA_pointer_create(NULL, &RNA_Context, C, &ctx_ptr);
3174
3175         /* check if we use primary or secondary path */
3176         if(!radial_control_get_path(&ctx_ptr, op, "use_secondary",
3177                                                                 &use_secondary_ptr, &use_secondary_prop,
3178                                                                 0, (RC_PROP_ALLOW_MISSING|
3179                                                                         RC_PROP_REQUIRE_BOOL))) {
3180                 return 0;
3181         }
3182         else {
3183                 if(use_secondary_prop &&
3184                    RNA_property_boolean_get(&use_secondary_ptr, use_secondary_prop))
3185                         data_path = "data_path_secondary";
3186                 else
3187                         data_path = "data_path_primary";
3188         }
3189
3190         if(!radial_control_get_path(&ctx_ptr, op, data_path, &rc->ptr, &rc->prop, 0, 0))
3191                 return 0;
3192
3193         /* data path is required */
3194         if(!rc->prop)
3195                 return 0;
3196         
3197         if(!radial_control_get_path(&ctx_ptr, op, "rotation_path", &rc->rot_ptr, &rc->rot_prop, 0, RC_PROP_REQUIRE_FLOAT))
3198                 return 0;
3199         if(!radial_control_get_path(&ctx_ptr, op, "color_path", &rc->col_ptr, &rc->col_prop, 3, RC_PROP_REQUIRE_FLOAT))
3200                 return 0;
3201         if(!radial_control_get_path(&ctx_ptr, op, "fill_color_path", &rc->fill_col_ptr, &rc->fill_col_prop, 3, RC_PROP_REQUIRE_FLOAT))
3202                 return 0;
3203         
3204         /* slightly ugly; allow this property to not resolve
3205            correctly. needed because 3d texture paint shares the same
3206            keymap as 2d image paint */
3207         if(!radial_control_get_path(&ctx_ptr, op, "zoom_path",
3208                                                                 &rc->zoom_ptr, &rc->zoom_prop, 2,
3209                                                                 RC_PROP_REQUIRE_FLOAT|RC_PROP_ALLOW_MISSING))
3210                 return 0;
3211         
3212         if(!radial_control_get_path(&ctx_ptr, op, "image_id", &rc->image_id_ptr, NULL, 0, 0))
3213                 return 0;
3214         else if(rc->image_id_ptr.data) {
3215                 /* extra check, pointer must be to an ID */
3216                 if(!RNA_struct_is_ID(rc->image_id_ptr.type)) {
3217                         BKE_report(op->reports, RPT_ERROR,
3218                                    "Pointer from path image_id is not an ID");
3219                         return 0;
3220                 }
3221         }
3222
3223         return 1;
3224 }
3225
3226 static int radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event)
3227 {
3228         wmWindowManager *wm;
3229         RadialControl *rc;
3230         int min_value_int, max_value_int, step_int;
3231         float step_float, precision;
3232
3233         if(!(op->customdata = rc = MEM_callocN(sizeof(RadialControl), "RadialControl")))
3234                 return OPERATOR_CANCELLED;
3235
3236         if(!radial_control_get_properties(C, op)) {
3237                 MEM_freeN(rc);
3238                 return OPERATOR_CANCELLED;
3239         }
3240
3241         /* get type, initial, min, and max values of the property */
3242         switch((rc->type = RNA_property_type(rc->prop))) {
3243         case PROP_INT:
3244                 rc->initial_value = RNA_property_int_get(&rc->ptr, rc->prop);
3245                 RNA_property_int_ui_range(&rc->ptr, rc->prop, &min_value_int,
3246                                           &max_value_int, &step_int);
3247                 rc->min_value = min_value_int;
3248                 rc->max_value = max_value_int;
3249                 break;
3250         case PROP_FLOAT:
3251                 rc->initial_value = RNA_property_float_get(&rc->ptr, rc->prop);
3252                 RNA_property_float_ui_range(&rc->ptr, rc->prop, &rc->min_value,
3253                                             &rc->max_value, &step_float, &precision);
3254                 break;
3255         default:
3256                 BKE_report(op->reports, RPT_ERROR, "Property must be an integer or a float");
3257                 MEM_freeN(rc);
3258                 return OPERATOR_CANCELLED;
3259         }
3260
3261         /* get subtype of property */
3262         rc->subtype = RNA_property_subtype(rc->prop);
3263         if(!ELEM3(rc->subtype, PROP_DISTANCE, PROP_FACTOR, PROP_ANGLE)) {
3264                 BKE_report(op->reports, RPT_ERROR, "Property must be a distance, a factor, or an angle");
3265                 MEM_freeN(rc);
3266                 return OPERATOR_CANCELLED;
3267         }
3268                 
3269         rc->current_value = rc->initial_value;
3270         radial_control_set_initial_mouse(rc, event);
3271         radial_control_set_tex(rc);
3272
3273         /* temporarily disable other paint cursors */
3274         wm = CTX_wm_manager(C);
3275         rc->orig_paintcursors = wm->paintcursors;
3276         wm->paintcursors.first = wm->paintcursors.last = NULL;
3277
3278         /* add radial control paint cursor */
3279         rc->cursor = WM_paint_cursor_activate(wm, op->type->poll,
3280                                               radial_control_paint_cursor, rc);
3281
3282         WM_event_add_modal_handler(C, op);
3283
3284         return OPERATOR_RUNNING_MODAL;
3285 }
3286
3287 static void radia