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