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