2a28a7edfe8632ef0c1574f31583645b5fb41c69
[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, __func__, UI_EMBOSS);
943         uiBlockClearFlag(block, UI_BLOCK_LOOP);
944         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
945
946         /* if register is not enabled, the operator gets freed on OPERATOR_FINISHED
947          * ui_apply_but_funcs_after calls ED_undo_operator_repeate_cb and crashes */
948         assert(op->type->flag & OPTYPE_REGISTER);
949
950         uiBlockSetHandleFunc(block, ED_undo_operator_repeat_cb_evt, arg_op);
951         layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, width, UI_UNIT_Y, style);
952
953         if (!WM_operator_check_ui_enabled(C, op->type->name))
954                 uiLayoutSetEnabled(layout, 0);
955
956         if(op->type->flag & OPTYPE_MACRO) {
957                 for(op= op->macro.first; op; op= op->next) {
958                         uiItemL(layout, op->type->name, ICON_NONE);
959                         uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
960                 }
961         }
962         else {
963                 uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
964         }
965         
966
967         uiPopupBoundsBlock(block, 4, 0, 0);
968         uiEndBlock(C, block);
969
970         return block;
971 }
972
973 typedef struct wmOpPopUp
974 {
975         wmOperator *op;
976         int width;
977         int height;
978         int free_op;
979 } wmOpPopUp;
980
981 /* Only invoked by OK button in popups created with wm_block_dialog_create() */
982 static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
983 {
984         wmOpPopUp *data= arg1;
985         uiBlock *block= arg2;
986
987         WM_operator_call(C, data->op);
988
989         /* let execute handle freeing it */
990         //data->free_op= FALSE;
991         //data->op= NULL;
992
993         /* in this case, wm_operator_ui_popup_cancel wont run */
994         MEM_freeN(data);
995
996         uiPupBlockClose(C, block);
997 }
998
999 static void dialog_check_cb(bContext *C, void *op_ptr, void *UNUSED(arg))
1000 {
1001         wmOperator *op= op_ptr;
1002         if(op->type->check) {
1003                 if(op->type->check(C, op)) {
1004                         /* refresh */
1005                 }
1006         }
1007 }
1008
1009 /* Dialogs are popups that require user verification (click OK) before exec */
1010 static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData)
1011 {
1012         wmOpPopUp *data= userData;
1013         wmOperator *op= data->op;
1014         uiBlock *block;
1015         uiLayout *layout;
1016         uiStyle *style= UI_GetStyle();
1017
1018         block = uiBeginBlock(C, ar, __func__, UI_EMBOSS);
1019         uiBlockClearFlag(block, UI_BLOCK_LOOP);
1020         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
1021
1022         layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, style);
1023         
1024         uiBlockSetFunc(block, dialog_check_cb, op, NULL);
1025
1026         uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
1027         
1028         /* clear so the OK button is left alone */
1029         uiBlockSetFunc(block, NULL, NULL, NULL);
1030
1031         /* new column so as not to interfear with custom layouts [#26436] */
1032         {
1033                 uiBlock *col_block;
1034                 uiLayout *col;
1035                 uiBut *btn;
1036
1037                 col= uiLayoutColumn(layout, FALSE);
1038                 col_block= uiLayoutGetBlock(col);
1039                 /* Create OK button, the callback of which will execute op */
1040                 btn= uiDefBut(col_block, BUT, 0, IFACE_("OK"), 0, -30, 0, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
1041                 uiButSetFunc(btn, dialog_exec_cb, data, col_block);
1042         }
1043
1044         /* center around the mouse */
1045         uiPopupBoundsBlock(block, 4, data->width/-2, data->height/2);
1046         uiEndBlock(C, block);
1047
1048         return block;
1049 }
1050
1051 static uiBlock *wm_operator_ui_create(bContext *C, ARegion *ar, void *userData)
1052 {
1053         wmOpPopUp *data= userData;
1054         wmOperator *op= data->op;
1055         uiBlock *block;
1056         uiLayout *layout;
1057         uiStyle *style= UI_GetStyle();
1058
1059         block= uiBeginBlock(C, ar, __func__, UI_EMBOSS);
1060         uiBlockClearFlag(block, UI_BLOCK_LOOP);
1061         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
1062
1063         layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, style);
1064
1065         /* since ui is defined the auto-layout args are not used */
1066         uiLayoutOperatorButs(C, layout, op, NULL, 'V', 0);
1067
1068         uiPopupBoundsBlock(block, 4, 0, 0);
1069         uiEndBlock(C, block);
1070
1071         return block;
1072 }
1073
1074 static void wm_operator_ui_popup_cancel(void *userData)
1075 {
1076         wmOpPopUp *data= userData;
1077         if(data->free_op && data->op) {
1078                 wmOperator *op= data->op;
1079                 WM_operator_free(op);
1080         }
1081
1082         MEM_freeN(data);
1083 }
1084
1085 static void wm_operator_ui_popup_ok(struct bContext *C, void *arg, int retval)
1086 {
1087         wmOpPopUp *data= arg;
1088         wmOperator *op= data->op;
1089
1090         if(op && retval > 0)
1091                 WM_operator_call(C, op);
1092 }
1093
1094 int WM_operator_ui_popup(bContext *C, wmOperator *op, int width, int height)
1095 {
1096         wmOpPopUp *data= MEM_callocN(sizeof(wmOpPopUp), "WM_operator_ui_popup");
1097         data->op= op;
1098         data->width= width;
1099         data->height= height;
1100         data->free_op= TRUE; /* if this runs and gets registered we may want not to free it */
1101         uiPupBlockEx(C, wm_operator_ui_create, NULL, wm_operator_ui_popup_cancel, data);
1102         return OPERATOR_RUNNING_MODAL;
1103 }
1104
1105 /* operator menu needs undo, for redo callback */
1106 int WM_operator_props_popup(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1107 {
1108         
1109         if((op->type->flag & OPTYPE_REGISTER)==0) {
1110                 BKE_reportf(op->reports, RPT_ERROR, "Operator '%s' does not have register enabled, incorrect invoke function.", op->type->idname);
1111                 return OPERATOR_CANCELLED;
1112         }
1113         
1114         ED_undo_push_op(C, op);
1115         wm_operator_register(C, op);
1116
1117         uiPupBlock(C, wm_block_create_redo, op);
1118
1119         return OPERATOR_RUNNING_MODAL;
1120 }
1121
1122 int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, int height)
1123 {
1124         wmOpPopUp *data= MEM_callocN(sizeof(wmOpPopUp), "WM_operator_props_dialog_popup");
1125         
1126         data->op= op;
1127         data->width= width;
1128         data->height= height;
1129         data->free_op= TRUE; /* if this runs and gets registered we may want not to free it */
1130
1131         /* op is not executed until popup OK but is clicked */
1132         uiPupBlockEx(C, wm_block_dialog_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data);
1133
1134         return OPERATOR_RUNNING_MODAL;
1135 }
1136
1137 int WM_operator_redo_popup(bContext *C, wmOperator *op)
1138 {
1139         /* CTX_wm_reports(C) because operator is on stack, not active in event system */
1140         if((op->type->flag & OPTYPE_REGISTER)==0) {
1141                 BKE_reportf(CTX_wm_reports(C), RPT_ERROR, "Operator redo '%s' does not have register enabled, incorrect invoke function.", op->type->idname);
1142                 return OPERATOR_CANCELLED;
1143         }
1144         if(op->type->poll && op->type->poll(C)==0) {
1145                 BKE_reportf(CTX_wm_reports(C), RPT_ERROR, "Operator redo '%s': wrong context.", op->type->idname);
1146                 return OPERATOR_CANCELLED;
1147         }
1148         
1149         uiPupBlock(C, wm_block_create_redo, op);
1150
1151         return OPERATOR_CANCELLED;
1152 }
1153
1154 /* ***************** Debug menu ************************* */
1155
1156 static int wm_debug_menu_exec(bContext *C, wmOperator *op)
1157 {
1158         G.rt= RNA_int_get(op->ptr, "debug_value");
1159         ED_screen_refresh(CTX_wm_manager(C), CTX_wm_window(C));
1160         WM_event_add_notifier(C, NC_WINDOW, NULL);
1161
1162         return OPERATOR_FINISHED;       
1163 }
1164
1165 static int wm_debug_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1166 {
1167         RNA_int_set(op->ptr, "debug_value", G.rt);
1168         return WM_operator_props_dialog_popup(C, op, 9*UI_UNIT_X, UI_UNIT_Y);
1169 }
1170
1171 static void WM_OT_debug_menu(wmOperatorType *ot)
1172 {
1173         ot->name= "Debug Menu";
1174         ot->idname= "WM_OT_debug_menu";
1175         ot->description= "Open a popup to set the debug level";
1176         
1177         ot->invoke= wm_debug_menu_invoke;
1178         ot->exec= wm_debug_menu_exec;
1179         ot->poll= WM_operator_winactive;
1180         
1181         RNA_def_int(ot->srna, "debug_value", 0, -10000, 10000, "Debug Value", "", INT_MIN, INT_MAX);
1182 }
1183
1184
1185 /* ***************** Splash Screen ************************* */
1186
1187 static void wm_block_splash_close(bContext *C, void *arg_block, void *UNUSED(arg))
1188 {
1189         uiPupBlockClose(C, arg_block);
1190 }
1191
1192 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *arg_unused);
1193
1194 /* XXX: hack to refresh splash screen with updated prest menu name,
1195  * since popup blocks don't get regenerated like panels do */
1196 static void wm_block_splash_refreshmenu (bContext *UNUSED(C), void *UNUSED(arg_block), void *UNUSED(arg))
1197 {
1198         /* ugh, causes crashes in other buttons, disabling for now until 
1199          * a better fix
1200         uiPupBlockClose(C, arg_block);
1201         uiPupBlock(C, wm_block_create_splash, NULL);
1202           */
1203 }
1204
1205 static int wm_resource_check_prev(void)
1206 {
1207
1208         char *res= BLI_get_folder_version(BLENDER_RESOURCE_PATH_USER, BLENDER_VERSION, TRUE);
1209
1210         // if(res) printf("USER: %s\n", res);
1211
1212 #if 0 /* ignore the local folder */
1213         if(res == NULL) {
1214                 /* with a local dir, copying old files isnt useful since local dir get priority for config */
1215                 res= BLI_get_folder_version(BLENDER_RESOURCE_PATH_LOCAL, BLENDER_VERSION, TRUE);
1216         }
1217 #endif
1218
1219         // if(res) printf("LOCAL: %s\n", res);
1220         if(res) {
1221                 return FALSE;
1222         }
1223         else {
1224                 return (BLI_get_folder_version(BLENDER_RESOURCE_PATH_USER, BLENDER_VERSION - 1, TRUE) != NULL);
1225         }
1226 }
1227
1228 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(arg))
1229 {
1230         uiBlock *block;
1231         uiBut *but;
1232         uiLayout *layout, *split, *col;
1233         uiStyle *style= UI_GetStyle();
1234         struct RecentFile *recent;
1235         int i;
1236         MenuType *mt= WM_menutype_find("USERPREF_MT_splash", TRUE);
1237         char url[96];
1238
1239 #ifndef WITH_HEADLESS
1240         extern char datatoc_splash_png[];
1241         extern int datatoc_splash_png_size;
1242
1243         ImBuf *ibuf= IMB_ibImageFromMemory((unsigned char*)datatoc_splash_png, datatoc_splash_png_size, IB_rect, "<splash screen>");
1244 #else
1245         ImBuf *ibuf= NULL;
1246 #endif
1247
1248
1249 #ifdef WITH_BUILDINFO
1250         int ver_width, rev_width;
1251         char *version_str = NULL;
1252         char *revision_str = NULL;
1253         char version_buf[128];
1254         char revision_buf[128];
1255         extern char build_rev[];
1256         
1257         version_str = &version_buf[0];
1258         revision_str = &revision_buf[0];
1259         
1260         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 #ifdef USE_BMESH_SAVE_AS_COMPAT
1977         if(RNA_boolean_get(op->ptr, "use_mesh_compat")) fileflags |=  G_FILE_MESH_COMPAT;
1978         else                                                                                    fileflags &= ~G_FILE_MESH_COMPAT;
1979 #endif
1980
1981         if ( WM_write_file(C, path, fileflags, op->reports, copy) != 0)
1982                 return OPERATOR_CANCELLED;
1983
1984         WM_event_add_notifier(C, NC_WM|ND_FILESAVE, NULL);
1985
1986         return OPERATOR_FINISHED;
1987 }
1988
1989 /* function used for WM_OT_save_mainfile too */
1990 static int blend_save_check(bContext *UNUSED(C), wmOperator *op)
1991 {
1992         char filepath[FILE_MAX];
1993         RNA_string_get(op->ptr, "filepath", filepath);
1994         if(!BLO_has_bfile_extension(filepath)) {
1995                 /* some users would prefer BLI_replace_extension(),
1996                  * we keep getting knit-picking bug reports about this - campbell */
1997                 BLI_ensure_extension(filepath, FILE_MAX, ".blend");
1998                 RNA_string_set(op->ptr, "filepath", filepath);
1999                 return TRUE;
2000         }
2001         return FALSE;
2002 }
2003
2004 static void WM_OT_save_as_mainfile(wmOperatorType *ot)
2005 {
2006         ot->name= "Save As Blender File";
2007         ot->idname= "WM_OT_save_as_mainfile";
2008         ot->description="Save the current file in the desired location";
2009         
2010         ot->invoke= wm_save_as_mainfile_invoke;
2011         ot->exec= wm_save_as_mainfile_exec;
2012         ot->check= blend_save_check;
2013         /* ommit window poll so this can work in background mode */
2014
2015         WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH);
2016         RNA_def_boolean(ot->srna, "compress", 0, "Compress", "Write compressed .blend file");
2017         RNA_def_boolean(ot->srna, "relative_remap", 1, "Remap Relative", "Remap relative paths when saving in a different directory");
2018         RNA_def_boolean(ot->srna, "copy", 0, "Save Copy", "Save a copy of the actual working state but does not make saved file active");
2019 #ifdef USE_BMESH_SAVE_AS_COMPAT
2020         RNA_def_boolean(ot->srna, "use_mesh_compat", 0, "Legacy Mesh Format", "Save using legacy mesh format (no ngons)");
2021 #endif
2022 }
2023
2024 /* *************** save file directly ******** */
2025
2026 static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2027 {
2028         char name[FILE_MAX];
2029         int check_existing=1;
2030         int ret;
2031         
2032         /* cancel if no active window */
2033         if (CTX_wm_window(C) == NULL)
2034                 return OPERATOR_CANCELLED;
2035
2036         save_set_compress(op);
2037
2038         /* if not saved before, get the name of the most recently used .blend file */
2039         if(G.main->name[0]==0 && G.recent_files.first) {
2040                 struct RecentFile *recent = G.recent_files.first;
2041                 BLI_strncpy(name, recent->filepath, FILE_MAX);
2042         }
2043         else
2044                 BLI_strncpy(name, G.main->name, FILE_MAX);
2045
2046         untitled(name);
2047         
2048         RNA_string_set(op->ptr, "filepath", name);
2049         
2050         if (RNA_struct_find_property(op->ptr, "check_existing"))
2051                 if (RNA_boolean_get(op->ptr, "check_existing")==0)
2052                         check_existing = 0;
2053         
2054         if (G.save_over) {
2055                 if (check_existing && BLI_exists(name)) {
2056                         uiPupMenuSaveOver(C, op, name);
2057                         ret= OPERATOR_RUNNING_MODAL;
2058                 }
2059                 else {
2060                         ret= wm_save_as_mainfile_exec(C, op);
2061                 }
2062         }
2063         else {
2064                 WM_event_add_fileselect(C, op);
2065                 ret= OPERATOR_RUNNING_MODAL;
2066         }
2067         
2068         return ret;
2069 }
2070
2071 static void WM_OT_save_mainfile(wmOperatorType *ot)
2072 {
2073         ot->name= "Save Blender File";
2074         ot->idname= "WM_OT_save_mainfile";
2075         ot->description="Save the current Blender file";
2076         
2077         ot->invoke= wm_save_mainfile_invoke;
2078         ot->exec= wm_save_as_mainfile_exec;
2079         ot->check= blend_save_check;
2080         /* ommit window poll so this can work in background mode */
2081         
2082         WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH);
2083         RNA_def_boolean(ot->srna, "compress", 0, "Compress", "Write compressed .blend file");
2084         RNA_def_boolean(ot->srna, "relative_remap", 0, "Remap Relative", "Remap relative paths when saving in a different directory");
2085 }
2086
2087 /* XXX: move these collada operators to a more appropriate place */
2088 #ifdef WITH_COLLADA
2089
2090 #include "../../collada/collada.h"
2091
2092 static int wm_collada_export_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2093 {       
2094         if(!RNA_property_is_set(op->ptr, "filepath")) {
2095                 char filepath[FILE_MAX];
2096                 BLI_strncpy(filepath, G.main->name, sizeof(filepath));
2097                 BLI_replace_extension(filepath, sizeof(filepath), ".dae");
2098                 RNA_string_set(op->ptr, "filepath", filepath);
2099         }
2100
2101         WM_event_add_fileselect(C, op);
2102
2103         return OPERATOR_RUNNING_MODAL;
2104 }
2105
2106 /* function used for WM_OT_save_mainfile too */
2107 static int wm_collada_export_exec(bContext *C, wmOperator *op)
2108 {
2109         char filename[FILE_MAX];
2110         int selected;
2111         
2112         if(!RNA_property_is_set(op->ptr, "filepath")) {
2113                 BKE_report(op->reports, RPT_ERROR, "No filename given");
2114                 return OPERATOR_CANCELLED;
2115         }
2116
2117         RNA_string_get(op->ptr, "filepath", filename);
2118         selected = RNA_boolean_get(op->ptr, "selected");
2119         if(collada_export(CTX_data_scene(C), filename, selected)) {
2120                 return OPERATOR_FINISHED;
2121         }
2122         else {
2123                 return OPERATOR_CANCELLED;
2124         }
2125 }
2126
2127 static void WM_OT_collada_export(wmOperatorType *ot)
2128 {
2129         ot->name= "Export COLLADA";
2130         ot->idname= "WM_OT_collada_export";
2131         
2132         ot->invoke= wm_collada_export_invoke;
2133         ot->exec= wm_collada_export_exec;
2134         ot->poll= WM_operator_winactive;
2135         
2136         WM_operator_properties_filesel(ot, FOLDERFILE|COLLADAFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH);
2137         RNA_def_boolean(ot->srna, "selected", 0, "Export only selected",
2138                 "Export only selected elements");
2139 }
2140
2141 /* function used for WM_OT_save_mainfile too */
2142 static int wm_collada_import_exec(bContext *C, wmOperator *op)
2143 {
2144         char filename[FILE_MAX];
2145         
2146         if(!RNA_property_is_set(op->ptr, "filepath")) {
2147                 BKE_report(op->reports, RPT_ERROR, "No filename given");
2148                 return OPERATOR_CANCELLED;
2149         }
2150
2151         RNA_string_get(op->ptr, "filepath", filename);
2152         if(collada_import(C, filename)) return OPERATOR_FINISHED;
2153         
2154         BKE_report(op->reports, RPT_ERROR, "Errors found during parsing COLLADA document. Please see console for error log.");
2155         
2156         return OPERATOR_FINISHED;
2157 }
2158
2159 static void WM_OT_collada_import(wmOperatorType *ot)
2160 {
2161         ot->name= "Import COLLADA";
2162         ot->idname= "WM_OT_collada_import";
2163         
2164         ot->invoke= WM_operator_filesel;
2165         ot->exec= wm_collada_import_exec;
2166         ot->poll= WM_operator_winactive;
2167         
2168         WM_operator_properties_filesel(ot, FOLDERFILE|COLLADAFILE, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH);
2169 }
2170
2171 #endif
2172
2173
2174 /* *********************** */
2175
2176 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
2177 {
2178         ot->name= "Toggle Fullscreen";
2179         ot->idname= "WM_OT_window_fullscreen_toggle";
2180         ot->description="Toggle the current window fullscreen";
2181
2182         ot->exec= wm_window_fullscreen_toggle_exec;
2183         ot->poll= WM_operator_winactive;
2184 }
2185
2186 static int wm_exit_blender_op(bContext *C, wmOperator *op)
2187 {
2188         WM_operator_free(op);
2189         
2190         WM_exit(C);     
2191         
2192         return OPERATOR_FINISHED;
2193 }
2194
2195 static void WM_OT_quit_blender(wmOperatorType *ot)
2196 {
2197         ot->name= "Quit Blender";
2198         ot->idname= "WM_OT_quit_blender";
2199         ot->description= "Quit Blender";
2200
2201         ot->invoke= WM_operator_confirm;
2202         ot->exec= wm_exit_blender_op;
2203         ot->poll= WM_operator_winactive;
2204 }
2205
2206 /* *********************** */
2207
2208 #if defined(WIN32)
2209
2210 static int wm_console_toggle_op(bContext *UNUSED(C), wmOperator *UNUSED(op))
2211 {
2212         GHOST_toggleConsole(2);
2213         return OPERATOR_FINISHED;
2214 }
2215
2216 static void WM_OT_console_toggle(wmOperatorType *ot)
2217 {
2218         ot->name= "Toggle System Console";
2219         ot->idname= "WM_OT_console_toggle";
2220         ot->description= "Toggle System Console";
2221         
2222         ot->exec= wm_console_toggle_op;
2223         ot->poll= WM_operator_winactive;
2224 }
2225
2226 #endif
2227
2228 /* ************ default paint cursors, draw always around cursor *********** */
2229 /*
2230  - returns handler to free 
2231  - poll(bContext): returns 1 if draw should happen
2232  - draw(bContext): drawing callback for paint cursor
2233 */
2234
2235 void *WM_paint_cursor_activate(wmWindowManager *wm, int (*poll)(bContext *C),
2236                                    wmPaintCursorDraw draw, void *customdata)
2237 {
2238         wmPaintCursor *pc= MEM_callocN(sizeof(wmPaintCursor), "paint cursor");
2239         
2240         BLI_addtail(&wm->paintcursors, pc);
2241         
2242         pc->customdata = customdata;
2243         pc->poll= poll;
2244         pc->draw= draw;
2245         
2246         return pc;
2247 }
2248
2249 void WM_paint_cursor_end(wmWindowManager *wm, void *handle)
2250 {
2251         wmPaintCursor *pc;
2252         
2253         for(pc= wm->paintcursors.first; pc; pc= pc->next) {
2254                 if(pc == (wmPaintCursor *)handle) {
2255                         BLI_remlink(&wm->paintcursors, pc);
2256                         MEM_freeN(pc);
2257                         return;
2258                 }
2259         }
2260 }
2261
2262 /* ************ window gesture operator-callback definitions ************** */
2263 /*
2264  * These are default callbacks for use in operators requiring gesture input
2265  */
2266
2267 /* **************** Border gesture *************** */
2268
2269 /* Border gesture has two types:
2270    1) WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border 
2271    2) WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends
2272
2273    It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type)
2274 */
2275
2276 static int border_apply_rect(wmOperator *op)
2277 {
2278         wmGesture *gesture= op->customdata;
2279         rcti *rect= gesture->customdata;
2280         
2281         if(rect->xmin==rect->xmax || rect->ymin==rect->ymax)
2282                 return 0;
2283
2284         
2285         /* operator arguments and storage. */
2286         RNA_int_set(op->ptr, "xmin", MIN2(rect->xmin, rect->xmax) );
2287         RNA_int_set(op->ptr, "ymin", MIN2(rect->ymin, rect->ymax) );
2288         RNA_int_set(op->ptr, "xmax", MAX2(rect->xmin, rect->xmax) );
2289         RNA_int_set(op->ptr, "ymax", MAX2(rect->ymin, rect->ymax) );
2290
2291         return 1;
2292 }
2293
2294 static int border_apply(bContext *C, wmOperator *op, int gesture_mode)
2295 {
2296         if (!border_apply_rect(op))
2297                 return 0;
2298         
2299         /* XXX weak; border should be configured for this without reading event types */
2300         if( RNA_struct_find_property(op->ptr, "gesture_mode") )
2301                 RNA_int_set(op->ptr, "gesture_mode", gesture_mode);
2302
2303         op->type->exec(C, op);
2304         return 1;
2305 }
2306
2307 static void wm_gesture_end(bContext *C, wmOperator *op)
2308 {
2309         wmGesture *gesture= op->customdata;
2310         
2311         WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
2312         op->customdata= NULL;
2313
2314         ED_area_tag_redraw(CTX_wm_area(C));
2315         
2316         if( RNA_struct_find_property(op->ptr, "cursor") )
2317                 WM_cursor_restore(CTX_wm_window(C));
2318 }
2319
2320 int WM_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
2321 {
2322         if(ISTWEAK(event->type))
2323                 op->customdata= WM_gesture_new(C, event, WM_GESTURE_RECT);
2324         else
2325                 op->customdata= WM_gesture_new(C, event, WM_GESTURE_CROSS_RECT);
2326
2327         /* add modal handler */
2328         WM_event_add_modal_handler(C, op);
2329         
2330         wm_gesture_tag_redraw(C);
2331
2332         return OPERATOR_RUNNING_MODAL;
2333 }
2334
2335 int WM_border_select_modal(bContext *C, wmOperator *op, wmEvent *event)
2336 {
2337         wmGesture *gesture= op->customdata;
2338         rcti *rect= gesture->customdata;
2339         int sx, sy;
2340         
2341         if(event->type== MOUSEMOVE) {
2342                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2343
2344                 if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
2345                         rect->xmin= rect->xmax= event->x - sx;
2346                         rect->ymin= rect->ymax= event->y - sy;
2347                 }
2348                 else {
2349                         rect->xmax= event->x - sx;
2350                         rect->ymax= event->y - sy;
2351                 }
2352                 border_apply_rect(op);
2353
2354                 wm_gesture_tag_redraw(C);
2355         }
2356         else if (event->type==EVT_MODAL_MAP) {
2357                 switch (event->val) {
2358                 case GESTURE_MODAL_BEGIN:
2359                         if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
2360                                 gesture->mode= 1;
2361                                 wm_gesture_tag_redraw(C);
2362                         }
2363                         break;
2364                 case GESTURE_MODAL_SELECT:
2365                 case GESTURE_MODAL_DESELECT:
2366                 case GESTURE_MODAL_IN:
2367                 case GESTURE_MODAL_OUT:
2368                         if(border_apply(C, op, event->val)) {
2369                                 wm_gesture_end(C, op);
2370                                 return OPERATOR_FINISHED;
2371                         }
2372                         wm_gesture_end(C, op);
2373                         return OPERATOR_CANCELLED;
2374                         break;
2375
2376                 case GESTURE_MODAL_CANCEL:
2377                         wm_gesture_end(C, op);
2378                         return OPERATOR_CANCELLED;
2379                 }
2380
2381         }
2382 //      // Allow view navigation???
2383 //      else {
2384 //              return OPERATOR_PASS_THROUGH;
2385 //      }
2386
2387         return OPERATOR_RUNNING_MODAL;
2388 }
2389
2390 int WM_border_select_cancel(bContext *C, wmOperator *op)
2391 {
2392         wm_gesture_end(C, op);
2393
2394         return OPERATOR_CANCELLED;
2395 }
2396
2397 /* **************** circle gesture *************** */
2398 /* works now only for selection or modal paint stuff, calls exec while hold mouse, exit on release */
2399
2400 #ifdef GESTURE_MEMORY
2401 int circle_select_size= 25; // XXX - need some operator memory thing\!
2402 #endif
2403
2404 int WM_gesture_circle_invoke(bContext *C, wmOperator *op, wmEvent *event)
2405 {
2406         op->customdata= WM_gesture_new(C, event, WM_GESTURE_CIRCLE);
2407         
2408         /* add modal handler */
2409         WM_event_add_modal_handler(C, op);
2410         
2411         wm_gesture_tag_redraw(C);
2412         
2413         return OPERATOR_RUNNING_MODAL;
2414 }
2415
2416 static void gesture_circle_apply(bContext *C, wmOperator *op)
2417 {
2418         wmGesture *gesture= op->customdata;
2419         rcti *rect= gesture->customdata;
2420         
2421         if(RNA_int_get(op->ptr, "gesture_mode")==GESTURE_MODAL_NOP)
2422                 return;
2423
2424         /* operator arguments and storage. */
2425         RNA_int_set(op->ptr, "x", rect->xmin);
2426         RNA_int_set(op->ptr, "y", rect->ymin);
2427         RNA_int_set(op->ptr, "radius", rect->xmax);
2428         
2429         if(op->type->exec)
2430                 op->type->exec(C, op);
2431 #ifdef GESTURE_MEMORY
2432         circle_select_size= rect->xmax;
2433 #endif
2434 }
2435
2436 int WM_gesture_circle_modal(bContext *C, wmOperator *op, wmEvent *event)
2437 {
2438         wmGesture *gesture= op->customdata;
2439         rcti *rect= gesture->customdata;
2440         int sx, sy;
2441
2442         if(event->type== MOUSEMOVE) {
2443                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2444
2445                 rect->xmin= event->x - sx;
2446                 rect->ymin= event->y - sy;
2447
2448                 wm_gesture_tag_redraw(C);
2449
2450                 if(gesture->mode)
2451                         gesture_circle_apply(C, op);
2452         }
2453         else if (event->type==EVT_MODAL_MAP) {
2454                 switch (event->val) {
2455                 case GESTURE_MODAL_CIRCLE_ADD:
2456                         rect->xmax += 2 + rect->xmax/10;
2457                         wm_gesture_tag_redraw(C);
2458                         break;
2459                 case GESTURE_MODAL_CIRCLE_SUB:
2460                         rect->xmax -= 2 + rect->xmax/10;
2461                         if(rect->xmax < 1) rect->xmax= 1;
2462                         wm_gesture_tag_redraw(C);
2463                         break;
2464                 case GESTURE_MODAL_SELECT:
2465                 case GESTURE_MODAL_DESELECT:
2466                 case GESTURE_MODAL_NOP:
2467                         if(RNA_struct_find_property(op->ptr, "gesture_mode"))
2468                                 RNA_int_set(op->ptr, "gesture_mode", event->val);
2469
2470                         if(event->val != GESTURE_MODAL_NOP) {
2471                                 /* apply first click */
2472                                 gesture_circle_apply(C, op);
2473                                 gesture->mode= 1;
2474                                 wm_gesture_tag_redraw(C);
2475                         }
2476                         break;
2477
2478                 case GESTURE_MODAL_CANCEL:
2479                 case GESTURE_MODAL_CONFIRM:
2480                         wm_gesture_end(C, op);
2481                         return OPERATOR_FINISHED; /* use finish or we dont get an undo */
2482                 }
2483         }
2484 //      // Allow view navigation???
2485 //      else {
2486 //              return OPERATOR_PASS_THROUGH;
2487 //      }
2488
2489         return OPERATOR_RUNNING_MODAL;
2490 }
2491
2492 int WM_gesture_circle_cancel(bContext *C, wmOperator *op)
2493 {
2494         wm_gesture_end(C, op);
2495
2496         return OPERATOR_CANCELLED;
2497 }
2498
2499 #if 0
2500 /* template to copy from */
2501 void WM_OT_circle_gesture(wmOperatorType *ot)
2502 {
2503         ot->name= "Circle Gesture";
2504         ot->idname= "WM_OT_circle_gesture";
2505         ot->description="Enter rotate mode with a circular gesture";
2506         
2507         ot->invoke= WM_gesture_circle_invoke;
2508         ot->modal= WM_gesture_circle_modal;
2509         
2510         ot->poll= WM_operator_winactive;
2511         
2512         RNA_def_property(ot->srna, "x", PROP_INT, PROP_NONE);
2513         RNA_def_property(ot->srna, "y", PROP_INT, PROP_NONE);
2514         RNA_def_property(ot->srna, "radius", PROP_INT, PROP_NONE);
2515
2516 }
2517 #endif
2518
2519 /* **************** Tweak gesture *************** */
2520
2521 static void tweak_gesture_modal(bContext *C, wmEvent *event)
2522 {
2523         wmWindow *window= CTX_wm_window(C);
2524         wmGesture *gesture= window->tweak;
2525         rcti *rect= gesture->customdata;
2526         int sx, sy, val;
2527         
2528         switch(event->type) {
2529                 case MOUSEMOVE:
2530                 case INBETWEEN_MOUSEMOVE:
2531                         
2532                         wm_subwindow_getorigin(window, gesture->swinid, &sx, &sy);
2533                         
2534                         rect->xmax= event->x - sx;
2535                         rect->ymax= event->y - sy;
2536                         
2537                         if((val= wm_gesture_evaluate(gesture))) {
2538                                 wmEvent tevent;
2539
2540                                 tevent= *(window->eventstate);
2541                                 if(gesture->event_type==LEFTMOUSE)
2542                                         tevent.type= EVT_TWEAK_L;
2543                                 else if(gesture->event_type==RIGHTMOUSE)
2544                                         tevent.type= EVT_TWEAK_R;
2545                                 else
2546                                         tevent.type= EVT_TWEAK_M;
2547                                 tevent.val= val;
2548                                 /* mouse coords! */
2549                                 wm_event_add(window, &tevent);
2550                                 
2551                                 WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
2552                         }
2553                         
2554                         break;
2555                         
2556                 case LEFTMOUSE:
2557                 case RIGHTMOUSE:
2558                 case MIDDLEMOUSE:
2559                         if(gesture->event_type==event->type) {
2560                                 WM_gesture_end(C, gesture);
2561
2562                                 /* when tweak fails we should give the other keymap entries a chance */
2563                                 event->val= KM_RELEASE;
2564                         }
2565                         break;
2566                 default:
2567                         if(!ISTIMER(event->type)) {
2568                                 WM_gesture_end(C, gesture);
2569                         }
2570                         break;
2571         }
2572 }
2573
2574 /* standard tweak, called after window handlers passed on event */
2575 void wm_tweakevent_test(bContext *C, wmEvent *event, int action)
2576 {
2577         wmWindow *win= CTX_wm_window(C);
2578         
2579         if(win->tweak==NULL) {
2580                 if(CTX_wm_region(C)) {
2581                         if(event->val==KM_PRESS) { 
2582                                 if( ELEM3(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) )
2583                                         win->tweak= WM_gesture_new(C, event, WM_GESTURE_TWEAK);
2584                         }
2585                 }
2586         }
2587         else {
2588                 /* no tweaks if event was handled */
2589                 if((action & WM_HANDLER_BREAK)) {
2590                         WM_gesture_end(C, win->tweak);
2591                 }
2592                 else
2593                         tweak_gesture_modal(C, event);
2594         }
2595 }
2596
2597 /* *********************** lasso gesture ****************** */
2598
2599 int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, wmEvent *event)
2600 {
2601         op->customdata= WM_gesture_new(C, event, WM_GESTURE_LASSO);
2602         
2603         /* add modal handler */
2604         WM_event_add_modal_handler(C, op);
2605         
2606         wm_gesture_tag_redraw(C);
2607         
2608         if( RNA_struct_find_property(op->ptr, "cursor") )
2609                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2610         
2611         return OPERATOR_RUNNING_MODAL;
2612 }
2613
2614 int WM_gesture_lines_invoke(bContext *C, wmOperator *op, wmEvent *event)
2615 {
2616         op->customdata= WM_gesture_new(C, event, WM_GESTURE_LINES);
2617         
2618         /* add modal handler */
2619         WM_event_add_modal_handler(C, op);
2620         
2621         wm_gesture_tag_redraw(C);
2622         
2623         if( RNA_struct_find_property(op->ptr, "cursor") )
2624                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2625         
2626         return OPERATOR_RUNNING_MODAL;
2627 }
2628
2629
2630 static void gesture_lasso_apply(bContext *C, wmOperator *op)
2631 {
2632         wmGesture *gesture= op->customdata;
2633         PointerRNA itemptr;
2634         float loc[2];
2635         int i;
2636         short *lasso= gesture->customdata;
2637         
2638         /* operator storage as path. */
2639
2640         for(i=0; i<gesture->points; i++, lasso+=2) {
2641                 loc[0]= lasso[0];
2642                 loc[1]= lasso[1];
2643                 RNA_collection_add(op->ptr, "path", &itemptr);
2644                 RNA_float_set_array(&itemptr, "loc", loc);
2645         }
2646         
2647         wm_gesture_end(C, op);
2648                 
2649         if(op->type->exec)
2650                 op->type->exec(C, op);
2651 }
2652
2653 int WM_gesture_lasso_modal(bContext *C, wmOperator *op, wmEvent *event)
2654 {
2655         wmGesture *gesture= op->customdata;
2656         int sx, sy;
2657         
2658         switch(event->type) {
2659                 case MOUSEMOVE:
2660                 case INBETWEEN_MOUSEMOVE:
2661                         
2662                         wm_gesture_tag_redraw(C);
2663                         
2664                         wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2665
2666                         if(gesture->points == gesture->size) {
2667                                 short *old_lasso = gesture->customdata;
2668                                 gesture->customdata= MEM_callocN(2*sizeof(short)*(gesture->size + WM_LASSO_MIN_POINTS), "lasso points");
2669                                 memcpy(gesture->customdata, old_lasso, 2*sizeof(short)*gesture->size);
2670                                 gesture->size = gesture->size + WM_LASSO_MIN_POINTS;
2671                                 MEM_freeN(old_lasso);
2672                                 // printf("realloc\n");
2673                         }
2674
2675                         {
2676                                 int x, y;
2677                                 short *lasso= gesture->customdata;
2678                                 
2679                                 lasso += (2 * gesture->points - 2);
2680                                 x = (event->x - sx - lasso[0]);
2681                                 y = (event->y - sy - lasso[1]);
2682                                 
2683                                 /* make a simple distance check to get a smoother lasso
2684                                    add only when at least 2 pixels between this and previous location */
2685                                 if((x*x+y*y) > 4) {
2686                                         lasso += 2;
2687                                         lasso[0] = event->x - sx;
2688                                         lasso[1] = event->y - sy;
2689                                         gesture->points++;
2690                                 }
2691                         }
2692                         break;
2693                         
2694                 case LEFTMOUSE:
2695                 case MIDDLEMOUSE:
2696                 case RIGHTMOUSE:
2697                         if(event->val==KM_RELEASE) {    /* key release */
2698                                 gesture_lasso_apply(C, op);
2699                                 return OPERATOR_FINISHED;
2700                         }
2701                         break;
2702                 case ESCKEY:
2703                         wm_gesture_end(C, op);
2704                         return OPERATOR_CANCELLED;
2705         }
2706         return OPERATOR_RUNNING_MODAL;
2707 }
2708
2709 int WM_gesture_lines_modal(bContext *C, wmOperator *op, wmEvent *event)
2710 {
2711         return WM_gesture_lasso_modal(C, op, event);
2712 }
2713
2714 int WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
2715 {
2716         wm_gesture_end(C, op);
2717
2718         return OPERATOR_CANCELLED;
2719 }
2720
2721 int WM_gesture_lines_cancel(bContext *C, wmOperator *op)
2722 {
2723         wm_gesture_end(C, op);
2724
2725         return OPERATOR_CANCELLED;
2726 }
2727
2728 #if 0
2729 /* template to copy from */
2730
2731 static int gesture_lasso_exec(bContext *C, wmOperator *op)
2732 {
2733         RNA_BEGIN(op->ptr, itemptr, "path") {
2734                 float loc[2];
2735                 
2736                 RNA_float_get_array(&itemptr, "loc", loc);
2737                 printf("Location: %f %f\n", loc[0], loc[1]);
2738         }
2739         RNA_END;
2740         
2741         return OPERATOR_FINISHED;
2742 }
2743
2744 void WM_OT_lasso_gesture(wmOperatorType *ot)
2745 {
2746         PropertyRNA *prop;
2747         
2748         ot->name= "Lasso Gesture";
2749         ot->idname= "WM_OT_lasso_gesture";
2750         ot->description="Select objects within the lasso as you move the pointer";
2751         
2752         ot->invoke= WM_gesture_lasso_invoke;
2753         ot->modal= WM_gesture_lasso_modal;
2754         ot->exec= gesture_lasso_exec;
2755         
2756         ot->poll= WM_operator_winactive;
2757         
2758         prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
2759         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
2760 }
2761 #endif
2762
2763 /* *********************** straight line gesture ****************** */
2764
2765 static int straightline_apply(bContext *C, wmOperator *op)
2766 {
2767         wmGesture *gesture= op->customdata;
2768         rcti *rect= gesture->customdata;
2769         
2770         if(rect->xmin==rect->xmax && rect->ymin==rect->ymax)
2771                 return 0;
2772         
2773         /* operator arguments and storage. */
2774         RNA_int_set(op->ptr, "xstart", rect->xmin);
2775         RNA_int_set(op->ptr, "ystart", rect->ymin);
2776         RNA_int_set(op->ptr, "xend", rect->xmax);
2777         RNA_int_set(op->ptr, "yend", rect->ymax);
2778
2779         if(op->type->exec)
2780                 op->type->exec(C, op);
2781         
2782         return 1;
2783 }
2784
2785
2786 int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, wmEvent *event)
2787 {
2788         op->customdata= WM_gesture_new(C, event, WM_GESTURE_STRAIGHTLINE);
2789         
2790         /* add modal handler */
2791         WM_event_add_modal_handler(C, op);
2792         
2793         wm_gesture_tag_redraw(C);
2794         
2795         if( RNA_struct_find_property(op->ptr, "cursor") )
2796                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2797                 
2798         return OPERATOR_RUNNING_MODAL;
2799 }
2800
2801 int WM_gesture_straightline_modal(bContext *C, wmOperator *op, wmEvent *event)
2802 {
2803         wmGesture *gesture= op->customdata;
2804         rcti *rect= gesture->customdata;
2805         int sx, sy;
2806         
2807         if(event->type== MOUSEMOVE) {
2808                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2809                 
2810                 if(gesture->mode==0) {
2811                         rect->xmin= rect->xmax= event->x - sx;
2812                         rect->ymin= rect->ymax= event->y - sy;
2813                 }
2814                 else {
2815                         rect->xmax= event->x - sx;
2816                         rect->ymax= event->y - sy;
2817                         straightline_apply(C, op);
2818                 }
2819                 
2820                 wm_gesture_tag_redraw(C);
2821         }
2822         else if (event->type==EVT_MODAL_MAP) {
2823                 switch (event->val) {
2824                         case GESTURE_MODAL_BEGIN:
2825                                 if(gesture->mode==0) {
2826                                         gesture->mode= 1;
2827                                         wm_gesture_tag_redraw(C);
2828                                 }
2829                                 break;
2830                         case GESTURE_MODAL_SELECT:
2831                                 if(straightline_apply(C, op)) {
2832                                         wm_gesture_end(C, op);
2833                                         return OPERATOR_FINISHED;
2834                                 }
2835                                 wm_gesture_end(C, op);
2836                                 return OPERATOR_CANCELLED;
2837                                 break;
2838                                 
2839                         case GESTURE_MODAL_CANCEL:
2840                                 wm_gesture_end(C, op);
2841                                 return OPERATOR_CANCELLED;
2842                 }
2843                 
2844         }
2845
2846         return OPERATOR_RUNNING_MODAL;
2847 }
2848
2849 int WM_gesture_straightline_cancel(bContext *C, wmOperator *op)
2850 {
2851         wm_gesture_end(C, op);
2852
2853         return OPERATOR_CANCELLED;
2854 }
2855
2856 #if 0
2857 /* template to copy from */
2858 void WM_OT_straightline_gesture(wmOperatorType *ot)
2859 {
2860         PropertyRNA *prop;
2861         
2862         ot->name= "Straight Line Gesture";
2863         ot->idname= "WM_OT_straightline_gesture";
2864         ot->description="Draw a straight line as you move the pointer";
2865         
2866         ot->invoke= WM_gesture_straightline_invoke;
2867         ot->modal= WM_gesture_straightline_modal;
2868         ot->exec= gesture_straightline_exec;
2869         
2870         ot->poll= WM_operator_winactive;
2871         
2872         WM_operator_properties_gesture_straightline(ot, 0);
2873 }
2874 #endif
2875
2876 /* *********************** radial control ****************** */
2877
2878 static const int WM_RADIAL_CONTROL_DISPLAY_SIZE = 200;
2879
2880 typedef struct {
2881         PropertyType type;
2882         PropertySubType subtype;
2883         PointerRNA ptr, col_ptr, fill_col_ptr, rot_ptr, zoom_ptr, image_id_ptr;
2884         PropertyRNA *prop, *col_prop, *fill_col_prop, *rot_prop, *zoom_prop;
2885         StructRNA *image_id_srna;
2886         float initial_value, current_value, min_value, max_value;
2887         int initial_mouse[2];
2888         unsigned int gltex;
2889         ListBase orig_paintcursors;
2890         void *cursor;
2891 } RadialControl;
2892
2893 static void radial_control_set_initial_mouse(RadialControl *rc, wmEvent *event)
2894 {
2895         float d[2] = {0, 0};
2896         float zoom[2] = {1, 1};
2897
2898         rc->initial_mouse[0]= event->x;
2899         rc->initial_mouse[1]= event->y;
2900
2901         switch(rc->subtype) {
2902         case PROP_DISTANCE:
2903                 d[0] = rc->initial_value;
2904                 break;
2905         case PROP_FACTOR:
2906                 d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * (1 - rc->initial_value);
2907                 break;
2908         case PROP_ANGLE:
2909                 d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * cos(rc->initial_value);
2910                 d[1] = WM_RADIAL_CONTROL_DISPLAY_SIZE * sin(rc->initial_value);
2911                 break;
2912         default:
2913                 return;
2914         }
2915
2916         if(rc->zoom_prop) {
2917                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2918                 d[0] *= zoom[0];
2919                 d[1] *= zoom[1];
2920         }
2921
2922         rc->initial_mouse[0]-= d[0];
2923         rc->initial_mouse[1]-= d[1];
2924 }
2925
2926 static void radial_control_set_tex(RadialControl *rc)
2927 {
2928         ImBuf *ibuf;
2929
2930         switch(RNA_type_to_ID_code(rc->image_id_ptr.type)) {
2931         case ID_BR:
2932                 if((ibuf = brush_gen_radial_control_imbuf(rc->image_id_ptr.data))) {
2933                         glGenTextures(1, &rc->gltex);
2934                         glBindTexture(GL_TEXTURE_2D, rc->gltex);
2935                         glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, ibuf->x, ibuf->y, 0,
2936                                      GL_ALPHA, GL_FLOAT, ibuf->rect_float);
2937                         MEM_freeN(ibuf->rect_float);
2938                         MEM_freeN(ibuf);
2939                 }
2940                 break;
2941         default:
2942                 break;
2943         }
2944 }
2945
2946 static void radial_control_paint_tex(RadialControl *rc, float radius, float alpha)
2947 {
2948         float col[3] = {0, 0, 0};
2949         float rot;
2950
2951         /* set fill color */
2952         if(rc->fill_col_prop)
2953                 RNA_property_float_get_array(&rc->fill_col_ptr, rc->fill_col_prop, col);
2954         glColor4f(col[0], col[1], col[2], alpha);
2955
2956         if(rc->gltex) {
2957                 glBindTexture(GL_TEXTURE_2D, rc->gltex);
2958
2959                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2960                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2961
2962                 /* set up rotation if available */
2963                 if(rc->rot_prop) {
2964                         rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop);
2965                         glPushMatrix();
2966                         glRotatef(RAD2DEGF(rot), 0, 0, 1);
2967                 }
2968
2969                 /* draw textured quad */
2970                 glEnable(GL_TEXTURE_2D);
2971                 glBegin(GL_QUADS);
2972                 glTexCoord2f(0,0);
2973                 glVertex2f(-radius, -radius);
2974                 glTexCoord2f(1,0);
2975                 glVertex2f(radius, -radius);
2976                 glTexCoord2f(1,1);
2977                 glVertex2f(radius, radius);
2978                 glTexCoord2f(0,1);
2979                 glVertex2f(-radius, radius);
2980                 glEnd();
2981                 glDisable(GL_TEXTURE_2D);
2982
2983                 /* undo rotation */
2984                 if(rc->rot_prop)
2985                         glPopMatrix();
2986         }
2987         else {
2988                 /* flat color if no texture available */
2989                 glutil_draw_filled_arc(0, M_PI * 2, radius, 40);
2990         }
2991 }
2992
2993 static void radial_control_paint_cursor(bContext *C, int x, int y, void *customdata)
2994 {
2995         RadialControl *rc = customdata;
2996         ARegion *ar = CTX_wm_region(C);
2997         float r1=0.0f, r2=0.0f, tex_radius, alpha;
2998         float zoom[2], col[3] = {1, 1, 1};
2999
3000         switch(rc->subtype) {
3001         case PROP_DISTANCE:
3002                 r1= rc->current_value;
3003                 r2= rc->initial_value;
3004                 tex_radius= r1;
3005                 alpha = 0.75;
3006                 break;
3007         case PROP_FACTOR:
3008                 r1= (1 - rc->current_value) * WM_RADIAL_CONTROL_DISPLAY_SIZE;
3009                 r2= tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE;
3010                 alpha = rc->current_value / 2.0f + 0.5f;
3011                 break;
3012         case PROP_ANGLE:
3013                 r1= r2= tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE;
3014                 alpha = 0.75;
3015                 break;
3016         default:
3017                 tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE; /* note, this is a dummy value */
3018                 alpha = 0.75;
3019                 break;
3020         }
3021
3022         /* Keep cursor in the original place */
3023         x = rc->initial_mouse[0] - ar->winrct.xmin;
3024         y = rc->initial_mouse[1] - ar->winrct.ymin;
3025         glTranslatef((float)x, (float)y, 0.0f);
3026
3027         glEnable(GL_BLEND);
3028         glEnable(GL_LINE_SMOOTH);
3029
3030         /* apply zoom if available */
3031         if(rc->zoom_prop) {
3032                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
3033                 glScalef(zoom[0], zoom[1], 1);
3034         }
3035
3036         /* draw rotated texture */
3037         radial_control_paint_tex(rc, tex_radius, alpha);
3038
3039         /* set line color */
3040         if(rc->col_prop)
3041                 RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col);
3042         glColor4f(col[0], col[1], col[2], 0.5);
3043
3044         if(rc->subtype == PROP_ANGLE) {
3045                 glPushMatrix();
3046                 /* draw original angle line */
3047                 glRotatef(RAD2DEGF(rc->initial_value), 0, 0, 1);
3048                 fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
3049                 /* draw new angle line */
3050                 glRotatef(RAD2DEGF(rc->current_value - rc->initial_value), 0, 0, 1);
3051                 fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
3052                 glPopMatrix();
3053         }
3054
3055         /* draw circles on top */
3056         glutil_draw_lined_arc(0.0, (float)(M_PI*2.0), r1, 40);
3057         glutil_draw_lined_arc(0.0, (float)(M_PI*2.0), r2, 40);
3058
3059         glDisable(GL_BLEND);
3060         glDisable(GL_LINE_SMOOTH);
3061 }
3062
3063 /* attempt to retrieve the rna pointer/property from an rna path;
3064    returns 0 for failure, 1 for success, and also 1 if property is not
3065    set */
3066 static int radial_control_get_path(PointerRNA *ctx_ptr, wmOperator *op,
3067                                    const char *name, PointerRNA *r_ptr,
3068                                    PropertyRNA **r_prop, int req_float,
3069                                    int req_length, int allow_missing)
3070 {
3071         PropertyRNA *unused_prop;
3072         int len;
3073         char *str;
3074
3075         /* get an rna string path from the operator's properties */
3076         if(!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0)))
3077                 return 1;
3078
3079         if(str[0] == '\0') {
3080                 MEM_freeN(str);
3081                 return 1;
3082         }
3083
3084         if(!r_prop)
3085                 r_prop = &unused_prop;
3086
3087         /* get rna from path */
3088         if(!RNA_path_resolve(ctx_ptr, str, r_ptr, r_prop)) {
3089                 MEM_freeN(str);
3090                 if(allow_missing)
3091                         return 1;
3092                 else {
3093                         BKE_reportf(op->reports, RPT_ERROR, "Couldn't resolve path %s", name);
3094                         return 0;
3095                 }
3096         }
3097
3098         /* if property is expected to be a float, check its type */
3099         if(req_float) {
3100                 if(!(*r_prop) || (RNA_property_type(*r_prop) != PROP_FLOAT)) {
3101                         MEM_freeN(str);
3102                         BKE_reportf(op->reports, RPT_ERROR,
3103                                     "Property from path %s is not a float", name);
3104                         return 0;
3105                 }
3106         }
3107         
3108         /* check property's array length */
3109         if(*r_prop && (len = RNA_property_array_length(r_ptr, *r_prop)) != req_length) {
3110                 MEM_freeN(str);
3111                 BKE_reportf(op->reports, RPT_ERROR,
3112                             "Property from path %s has length %d instead of %d",
3113                             name, len, req_length);
3114                 return 0;
3115         }
3116
3117         /* success */
3118         MEM_freeN(str);
3119         return 1;
3120 }
3121
3122 /* initialize the rna pointers and properties using rna paths */
3123 static int radial_control_get_properties(bContext *C, wmOperator *op)
3124 {
3125         RadialControl *rc = op->customdata;
3126         PointerRNA ctx_ptr;
3127
3128         RNA_pointer_create(NULL, &RNA_Context, C, &ctx_ptr);
3129
3130         if(!radial_control_get_path(&ctx_ptr, op, "data_path", &rc->ptr, &rc->prop, 0, 0, 0))
3131                 return 0;
3132
3133         /* data path is required */
3134         if(!rc->prop)
3135                 return 0;
3136         
3137         if(!radial_control_get_path(&ctx_ptr, op, "rotation_path", &rc->rot_ptr, &rc->rot_prop, 1, 0, 0))
3138                 return 0;
3139         if(!radial_control_get_path(&ctx_ptr, op, "color_path", &rc->col_ptr, &rc->col_prop, 1, 3, 0))
3140                 return 0;
3141         if(!radial_control_get_path(&ctx_ptr, op, "fill_color_path", &rc->fill_col_ptr, &rc->fill_col_prop, 1, 3, 0))
3142                 return 0;
3143         
3144         /* slightly ugly; allow this property to not resolve
3145            correctly. needed because 3d texture paint shares the same
3146            keymap as 2d image paint */
3147         if(!radial_control_get_path(&ctx_ptr, op, "zoom_path", &rc->zoom_ptr, &rc->zoom_prop, 1, 2, 1))
3148                 return 0;
3149         
3150         if(!radial_control_get_path(&ctx_ptr, op, "image_id", &rc->image_id_ptr, NULL, 0, 0, 0))
3151                 return 0;
3152         else if(rc->image_id_ptr.data) {
3153                 /* extra check, pointer must be to an ID */
3154                 if(!RNA_struct_is_ID(rc->image_id_ptr.type)) {
3155                         BKE_report(op->reports, RPT_ERROR,
3156                                    "Pointer from path image_id is not an ID");
3157                         return 0;
3158                 }
3159         }
3160
3161         return 1;
3162 }
3163
3164 static int radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event)
3165 {
3166         wmWindowManager *wm;
3167         RadialControl *rc;
3168         int min_value_int, max_value_int, step_int;
3169         float step_float, precision;
3170
3171         if(!(op->customdata = rc = MEM_callocN(sizeof(RadialControl), "RadialControl")))
3172                 return OPERATOR_CANCELLED;
3173
3174         if(!radial_control_get_properties(C, op)) {
3175                 MEM_freeN(rc);
3176                 return OPERATOR_CANCELLED;
3177         }
3178
3179         /* get type, initial, min, and max values of the property */
3180         switch((rc->type = RNA_property_type(rc->prop))) {
3181         case PROP_INT:
3182                 rc->initial_value = RNA_property_int_get(&rc->ptr, rc->prop);
3183                 RNA_property_int_ui_range(&rc->ptr, rc->prop, &min_value_int,
3184                                           &max_value_int, &step_int);
3185                 rc->min_value = min_value_int;
3186                 rc->max_value = max_value_int;
3187                 break;
3188         case PROP_FLOAT:
3189                 rc->initial_value = RNA_property_float_get(&rc->ptr, rc->prop);
3190                 RNA_property_float_ui_range(&rc->ptr, rc->prop, &rc->min_value,
3191                                             &rc->max_value, &step_float, &precision);
3192                 break;
3193         default:
3194                 BKE_report(op->reports, RPT_ERROR, "Property must be an integer or a float");
3195                 MEM_freeN(rc);
3196                 return OPERATOR_CANCELLED;
3197         }
3198
3199         /* get subtype of property */
3200         rc->subtype = RNA_property_subtype(rc->prop);
3201         if(!ELEM3(rc->subtype, PROP_DISTANCE, PROP_FACTOR, PROP_ANGLE)) {
3202                 BKE_report(op->reports, RPT_ERROR, "Property must be a distance, a factor, or an angle");
3203                 MEM_freeN(rc);
3204                 return OPERATOR_CANCELLED;
3205         }
3206                 
3207         rc->current_value = rc->initial_value;
3208         radial_control_set_initial_mouse(rc, event);
3209         radial_control_set_tex(rc);
3210
3211         /* temporarily disable other paint cursors */
3212         wm = CTX_wm_manager(C);
3213         rc->orig_paintcursors = wm->paintcursors;
3214         wm->paintcursors.first = wm->paintcursors.last = NULL;
3215
3216         /* add radial control paint cursor */
3217         rc->cursor = WM_paint_cursor_activate(wm, op->type->poll,
3218                                               radial_control_paint_cursor, rc);
3219
3220         WM_event_add_modal_handler(C, op);
3221
3222         return OPERATOR_RUNNING_MODAL;
3223 }
3224
3225 static void radial_control_set_value(RadialControl *rc, float val)
3226 {
3227         switch(rc->type) {
3228         case PROP_INT:
3229                 RNA_property_int_set(&rc->ptr, rc->prop, val);
3230                 break;
3231         case PROP_FLOAT:
3232                 RNA_property_float_set(&rc->ptr, rc->prop, val);
3233                 break;
3234         default:
3235                 break;
3236         }
3237 }
3238
3239 static int radial_control_cancel(bContext *C, wmOperator *op)
3240 {
3241         RadialControl *rc = op->customdata;
3242         wmWindowManager *wm = CTX_wm_manager(C);
3243
3244         WM_paint_cursor_end(wm, rc->cursor);
3245
3246         /* restore original paint cursors */
3247         wm->paintcursors = rc->orig_paintcursors;
3248
3249         /* not sure if this is a good notifier to use;
3250            intended purpose is to update the UI so that the
3251            new value is displayed in sliders/numfields */
3252         WM_event_add_notifier(C, NC_WINDOW, NULL);
3253
3254         glDeleteTextures(1, &rc->gltex);
3255
3256         MEM_freeN(rc);
3257
3258         return OPERATOR_CANCELLED;
3259 }
3260
3261 static int radial_control_modal(bContext *C, wmOperator *op, wmEvent *event)
3262 {
3263         RadialControl *rc = op->customdata;
3264         float new_value, dist, zoom[2];
3265         float delta[2], snap, ret = OPERATOR_RUNNING_MODAL;
3266
3267         /* TODO: fix hardcoded events */
3268
3269         snap = event->ctrl;
3270
3271         switch(event->type) {
3272         case MOUSEMOVE:
3273                 delta[0]= rc->initial_mouse[0] - event->x;
3274                 delta[1]= rc->initial_mouse[1] - event->y;
3275
3276                 if(rc->zoom_prop) {
3277                         RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
3278                         delta[0] /= zoom[0];
3279                         delta[1] /= zoom[1];
3280                 }
3281
3282                 dist= sqrt(delta[0]*delta[0]+delta[1]*delta[1]);
3283
3284                 /* calculate new value and apply snapping  */
3285                 switch(rc->subtype) {
3286                 case PROP_DISTANCE:
3287                         new_value = dist;
3288                         if(snap) new_value = ((int)new_value + 5) / 10*10;
3289                         break;
3290                 case PROP_FACTOR:
3291                         new_value = 1 - dist / WM_RADIAL_CONTROL_DISPLAY_SIZE;
3292                         if(snap) new_value = ((int)ceil(new_value * 10.f) * 10.0f) / 100.f;
3293                         break;
3294    &n