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