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