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