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