20f6162f9d4d020b5fb30dd489d0a647f1fa0779
[blender.git] / source / blender / windowmanager / intern / wm_operators.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2007 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup wm
22  *
23  * Functions for dealing with wmOperator, adding, removing, calling
24  * as well as some generic operators and shared operator properties.
25  */
26
27 #include <float.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <stddef.h>
32 #include <assert.h>
33 #include <errno.h>
34
35 #ifdef WIN32
36 #  include "GHOST_C-api.h"
37 #endif
38
39 #include "MEM_guardedalloc.h"
40
41 #include "CLG_log.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_workspace_types.h"
50
51 #include "BLT_translation.h"
52
53 #include "PIL_time.h"
54
55 #include "BLI_blenlib.h"
56 #include "BLI_dial_2d.h"
57 #include "BLI_dynstr.h" /*for WM_operator_pystring */
58 #include "BLI_math.h"
59 #include "BLI_utildefines.h"
60
61 #include "BLO_readfile.h"
62
63 #include "BKE_appdir.h"
64 #include "BKE_blender_version.h"
65 #include "BKE_brush.h"
66 #include "BKE_context.h"
67 #include "BKE_global.h"
68 #include "BKE_icons.h"
69 #include "BKE_idprop.h"
70 #include "BKE_image.h"
71 #include "BKE_library.h"
72 #include "BKE_library_query.h"
73 #include "BKE_main.h"
74 #include "BKE_material.h"
75 #include "BKE_report.h"
76 #include "BKE_scene.h"
77 #include "BKE_screen.h" /* BKE_ST_MAXNAME */
78 #include "BKE_unit.h"
79
80 #include "BKE_idcode.h"
81
82 #include "BLF_api.h"
83
84 #include "GPU_immediate.h"
85 #include "GPU_immediate_util.h"
86 #include "GPU_matrix.h"
87 #include "GPU_state.h"
88
89 #include "IMB_imbuf_types.h"
90 #include "IMB_imbuf.h"
91
92 #include "ED_numinput.h"
93 #include "ED_screen.h"
94 #include "ED_undo.h"
95 #include "ED_view3d.h"
96
97 #include "RNA_access.h"
98 #include "RNA_define.h"
99 #include "RNA_enum_types.h"
100
101 #include "UI_interface.h"
102 #include "UI_interface_icons.h"
103 #include "UI_resources.h"
104
105 #include "WM_api.h"
106 #include "WM_types.h"
107
108 #include "wm.h"
109 #include "wm_draw.h"
110 #include "wm_event_system.h"
111 #include "wm_event_types.h"
112 #include "wm_files.h"
113 #include "wm_window.h"
114
115 #define UNDOCUMENTED_OPERATOR_TIP N_("(undocumented operator)")
116
117 /* ************ operator API, exported ********** */
118
119 /* SOME_OT_op -> some.op */
120 void WM_operator_py_idname(char *to, const char *from)
121 {
122   const char *sep = strstr(from, "_OT_");
123   if (sep) {
124     int ofs = (sep - from);
125
126     /* note, we use ascii tolower instead of system tolower, because the
127      * latter depends on the locale, and can lead to idname mismatch */
128     memcpy(to, from, sizeof(char) * ofs);
129     BLI_str_tolower_ascii(to, ofs);
130
131     to[ofs] = '.';
132     BLI_strncpy(to + (ofs + 1), sep + 4, OP_MAX_TYPENAME - (ofs + 1));
133   }
134   else {
135     /* should not happen but support just in case */
136     BLI_strncpy(to, from, OP_MAX_TYPENAME);
137   }
138 }
139
140 /* some.op -> SOME_OT_op */
141 void WM_operator_bl_idname(char *to, const char *from)
142 {
143   if (from) {
144     const char *sep = strchr(from, '.');
145
146     if (sep) {
147       int ofs = (sep - from);
148
149       memcpy(to, from, sizeof(char) * ofs);
150       BLI_str_toupper_ascii(to, ofs);
151       strcpy(to + ofs, "_OT_");
152       strcpy(to + (ofs + 4), sep + 1);
153     }
154     else {
155       /* should not happen but support just in case */
156       BLI_strncpy(to, from, OP_MAX_TYPENAME);
157     }
158   }
159   else {
160     to[0] = 0;
161   }
162 }
163
164 /**
165  * Sanity check to ensure #WM_operator_bl_idname won't fail.
166  * \returns true when there are no problems with \a idname, otherwise report an error.
167  */
168 bool WM_operator_py_idname_ok_or_report(ReportList *reports,
169                                         const char *classname,
170                                         const char *idname)
171 {
172   const char *ch = idname;
173   int dot = 0;
174   int i;
175   for (i = 0; *ch; i++, ch++) {
176     if ((*ch >= 'a' && *ch <= 'z') || (*ch >= '0' && *ch <= '9') || *ch == '_') {
177       /* pass */
178     }
179     else if (*ch == '.') {
180       dot++;
181     }
182     else {
183       BKE_reportf(reports,
184                   RPT_ERROR,
185                   "Registering operator class: '%s', invalid bl_idname '%s', at position %d",
186                   classname,
187                   idname,
188                   i);
189       return false;
190     }
191   }
192
193   if (i > (MAX_NAME - 3)) {
194     BKE_reportf(reports,
195                 RPT_ERROR,
196                 "Registering operator class: '%s', invalid bl_idname '%s', "
197                 "is too long, maximum length is %d",
198                 classname,
199                 idname,
200                 MAX_NAME - 3);
201     return false;
202   }
203
204   if (dot != 1) {
205     BKE_reportf(
206         reports,
207         RPT_ERROR,
208         "Registering operator class: '%s', invalid bl_idname '%s', must contain 1 '.' character",
209         classname,
210         idname);
211     return false;
212   }
213   return true;
214 }
215
216 /**
217  * Print a string representation of the operator, with the args that it runs so python can run it again.
218  *
219  * When calling from an existing wmOperator, better to use simple version:
220  * `WM_operator_pystring(C, op);`
221  *
222  * \note Both \a op and \a opptr may be `NULL` (\a op is only used for macro operators).
223  */
224 char *WM_operator_pystring_ex(bContext *C,
225                               wmOperator *op,
226                               const bool all_args,
227                               const bool macro_args,
228                               wmOperatorType *ot,
229                               PointerRNA *opptr)
230 {
231   char idname_py[OP_MAX_TYPENAME];
232
233   /* for building the string */
234   DynStr *dynstr = BLI_dynstr_new();
235   char *cstring;
236   char *cstring_args;
237
238   /* arbitrary, but can get huge string with stroke painting otherwise */
239   int max_prop_length = 10;
240
241   WM_operator_py_idname(idname_py, ot->idname);
242   BLI_dynstr_appendf(dynstr, "bpy.ops.%s(", idname_py);
243
244   if (op && op->macro.first) {
245     /* Special handling for macros, else we only get default values in this case... */
246     wmOperator *opm;
247     bool first_op = true;
248
249     opm = macro_args ? op->macro.first : NULL;
250
251     for (; opm; opm = opm->next) {
252       PointerRNA *opmptr = opm->ptr;
253       PointerRNA opmptr_default;
254       if (opmptr == NULL) {
255         WM_operator_properties_create_ptr(&opmptr_default, opm->type);
256         opmptr = &opmptr_default;
257       }
258
259       cstring_args = RNA_pointer_as_string_id(C, opmptr);
260       if (first_op) {
261         BLI_dynstr_appendf(dynstr, "%s=%s", opm->type->idname, cstring_args);
262         first_op = false;
263       }
264       else {
265         BLI_dynstr_appendf(dynstr, ", %s=%s", opm->type->idname, cstring_args);
266       }
267       MEM_freeN(cstring_args);
268
269       if (opmptr == &opmptr_default) {
270         WM_operator_properties_free(&opmptr_default);
271       }
272     }
273   }
274   else {
275     /* only to get the original props for comparisons */
276     PointerRNA opptr_default;
277     const bool macro_args_test = ot->macro.first ? macro_args : true;
278
279     if (opptr == NULL) {
280       WM_operator_properties_create_ptr(&opptr_default, ot);
281       opptr = &opptr_default;
282     }
283
284     cstring_args = RNA_pointer_as_string_keywords(
285         C, opptr, false, all_args, macro_args_test, max_prop_length);
286     BLI_dynstr_append(dynstr, cstring_args);
287     MEM_freeN(cstring_args);
288
289     if (opptr == &opptr_default) {
290       WM_operator_properties_free(&opptr_default);
291     }
292   }
293
294   BLI_dynstr_append(dynstr, ")");
295
296   cstring = BLI_dynstr_get_cstring(dynstr);
297   BLI_dynstr_free(dynstr);
298   return cstring;
299 }
300
301 char *WM_operator_pystring(bContext *C, wmOperator *op, const bool all_args, const bool macro_args)
302 {
303   return WM_operator_pystring_ex(C, op, all_args, macro_args, op->type, op->ptr);
304 }
305
306 /**
307  * \return true if the string was shortened
308  */
309 bool WM_operator_pystring_abbreviate(char *str, int str_len_max)
310 {
311   const int str_len = strlen(str);
312   const char *parens_start = strchr(str, '(');
313
314   if (parens_start) {
315     const int parens_start_pos = parens_start - str;
316     const char *parens_end = strrchr(parens_start + 1, ')');
317
318     if (parens_end) {
319       const int parens_len = parens_end - parens_start;
320
321       if (parens_len > str_len_max) {
322         const char *comma_first = strchr(parens_start, ',');
323
324         /* truncate after the first comma */
325         if (comma_first) {
326           const char end_str[] = " ... )";
327           const int end_str_len = sizeof(end_str) - 1;
328
329           /* leave a place for the first argument*/
330           const int new_str_len = (comma_first - parens_start) + 1;
331
332           if (str_len >= new_str_len + parens_start_pos + end_str_len + 1) {
333             /* append " ... )" to the string after the comma */
334             memcpy(str + new_str_len + parens_start_pos, end_str, end_str_len + 1);
335
336             return true;
337           }
338         }
339       }
340     }
341   }
342
343   return false;
344 }
345
346 /* return NULL if no match is found */
347 #if 0
348 static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
349 {
350   /* loop over all context items and do 2 checks
351    *
352    * - see if the pointer is in the context.
353    * - see if the pointers ID is in the context.
354    */
355
356   /* don't get from the context store since this is normally set only for the UI and not usable elsewhere */
357   ListBase lb = CTX_data_dir_get_ex(C, false, true, true);
358   LinkData *link;
359
360   const char *member_found = NULL;
361   const char *member_id = NULL;
362
363   for (link = lb.first; link; link = link->next) {
364     const char *identifier = link->data;
365     PointerRNA ctx_item_ptr = {
366         {0}};  // CTX_data_pointer_get(C, identifier); // XXX, this isnt working
367
368     if (ctx_item_ptr.type == NULL) {
369       continue;
370     }
371
372     if (ptr->id.data == ctx_item_ptr.id.data) {
373       if ((ptr->data == ctx_item_ptr.data) && (ptr->type == ctx_item_ptr.type)) {
374         /* found! */
375         member_found = identifier;
376         break;
377       }
378       else if (RNA_struct_is_ID(ctx_item_ptr.type)) {
379         /* we found a reference to this ID,
380          * so fallback to it if there is no direct reference */
381         member_id = identifier;
382       }
383     }
384   }
385   BLI_freelistN(&lb);
386
387   if (member_found) {
388     return member_found;
389   }
390   else if (member_id) {
391     return member_id;
392   }
393   else {
394     return NULL;
395   }
396 }
397
398 #else
399
400 /* use hard coded checks for now */
401
402 static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
403 {
404   const char *member_id = NULL;
405
406   if (ptr->id.data) {
407
408 #  define CTX_TEST_PTR_ID(C, member, idptr) \
409     { \
410       const char *ctx_member = member; \
411       PointerRNA ctx_item_ptr = CTX_data_pointer_get(C, ctx_member); \
412       if (ctx_item_ptr.id.data == idptr) { \
413         member_id = ctx_member; \
414         break; \
415       } \
416     } \
417     (void)0
418
419 #  define CTX_TEST_PTR_ID_CAST(C, member, member_full, cast, idptr) \
420     { \
421       const char *ctx_member = member; \
422       const char *ctx_member_full = member_full; \
423       PointerRNA ctx_item_ptr = CTX_data_pointer_get(C, ctx_member); \
424       if (ctx_item_ptr.id.data && cast(ctx_item_ptr.id.data) == idptr) { \
425         member_id = ctx_member_full; \
426         break; \
427       } \
428     } \
429     (void)0
430
431 #  define CTX_TEST_PTR_DATA_TYPE(C, member, rna_type, rna_ptr, dataptr_cmp) \
432     { \
433       const char *ctx_member = member; \
434       if (RNA_struct_is_a((ptr)->type, &(rna_type)) && (ptr)->data == (dataptr_cmp)) { \
435         member_id = ctx_member; \
436         break; \
437       } \
438     } \
439     (void)0
440
441 #  define CTX_TEST_SPACE_TYPE(space_data_type, member_full, dataptr_cmp) \
442     { \
443       const char *ctx_member_full = member_full; \
444       if (space_data->spacetype == space_data_type && ptr->data == dataptr_cmp) { \
445         member_id = ctx_member_full; \
446         break; \
447       } \
448     } \
449     (void)0
450
451     switch (GS(((ID *)ptr->id.data)->name)) {
452       case ID_SCE: {
453         CTX_TEST_PTR_ID(C, "scene", ptr->id.data);
454         break;
455       }
456       case ID_OB: {
457         CTX_TEST_PTR_ID(C, "object", ptr->id.data);
458         break;
459       }
460       /* from rna_Main_objects_new */
461       case OB_DATA_SUPPORT_ID_CASE: {
462 #  define ID_CAST_OBDATA(id_pt) (((Object *)(id_pt))->data)
463         CTX_TEST_PTR_ID_CAST(C, "object", "object.data", ID_CAST_OBDATA, ptr->id.data);
464         break;
465 #  undef ID_CAST_OBDATA
466       }
467       case ID_MA: {
468 #  define ID_CAST_OBMATACT(id_pt) \
469     (give_current_material(((Object *)id_pt), ((Object *)id_pt)->actcol))
470         CTX_TEST_PTR_ID_CAST(
471             C, "object", "object.active_material", ID_CAST_OBMATACT, ptr->id.data);
472         break;
473 #  undef ID_CAST_OBMATACT
474       }
475       case ID_WO: {
476 #  define ID_CAST_SCENEWORLD(id_pt) (((Scene *)(id_pt))->world)
477         CTX_TEST_PTR_ID_CAST(C, "scene", "scene.world", ID_CAST_SCENEWORLD, ptr->id.data);
478         break;
479 #  undef ID_CAST_SCENEWORLD
480       }
481       case ID_SCR: {
482         CTX_TEST_PTR_ID(C, "screen", ptr->id.data);
483
484         SpaceLink *space_data = CTX_wm_space_data(C);
485
486         CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_Space, ptr, space_data);
487         CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_View3DOverlay, ptr, space_data);
488         CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_View3DShading, ptr, space_data);
489         CTX_TEST_PTR_DATA_TYPE(C, "area", RNA_Area, ptr, CTX_wm_area(C));
490         CTX_TEST_PTR_DATA_TYPE(C, "region", RNA_Region, ptr, CTX_wm_region(C));
491
492         CTX_TEST_SPACE_TYPE(SPACE_IMAGE, "space_data.uv_editor", space_data);
493         CTX_TEST_SPACE_TYPE(
494             SPACE_VIEW3D, "space_data.fx_settings", &(CTX_wm_view3d(C)->fx_settings));
495         CTX_TEST_SPACE_TYPE(SPACE_NLA, "space_data.dopesheet", CTX_wm_space_nla(C)->ads);
496         CTX_TEST_SPACE_TYPE(SPACE_GRAPH, "space_data.dopesheet", CTX_wm_space_graph(C)->ads);
497         CTX_TEST_SPACE_TYPE(SPACE_ACTION, "space_data.dopesheet", &(CTX_wm_space_action(C)->ads));
498         CTX_TEST_SPACE_TYPE(SPACE_FILE, "space_data.params", CTX_wm_space_file(C)->params);
499         break;
500       }
501       default:
502         break;
503     }
504 #  undef CTX_TEST_PTR_ID
505 #  undef CTX_TEST_PTR_ID_CAST
506 #  undef CTX_TEST_SPACE_TYPE
507   }
508
509   return member_id;
510 }
511 #endif
512
513 static char *wm_prop_pystring_from_context(bContext *C,
514                                            PointerRNA *ptr,
515                                            PropertyRNA *prop,
516                                            int index)
517 {
518   const char *member_id = wm_context_member_from_ptr(C, ptr);
519   char *ret = NULL;
520   if (member_id != NULL) {
521     char *prop_str = RNA_path_struct_property_py(ptr, prop, index);
522     if (prop_str) {
523       ret = BLI_sprintfN("bpy.context.%s.%s", member_id, prop_str);
524       MEM_freeN(prop_str);
525     }
526   }
527   return ret;
528 }
529
530 const char *WM_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
531 {
532   return wm_context_member_from_ptr(C, ptr);
533 }
534
535 char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index)
536 {
537   char *lhs, *rhs, *ret;
538
539   lhs = C ? wm_prop_pystring_from_context(C, ptr, prop, index) : NULL;
540
541   if (lhs == NULL) {
542     /* fallback to bpy.data.foo[id] if we dont find in the context */
543     lhs = RNA_path_full_property_py(ptr, prop, index);
544   }
545
546   if (!lhs) {
547     return NULL;
548   }
549
550   rhs = RNA_property_as_string(C, ptr, prop, index, INT_MAX);
551   if (!rhs) {
552     MEM_freeN(lhs);
553     return NULL;
554   }
555
556   ret = BLI_sprintfN("%s = %s", lhs, rhs);
557   MEM_freeN(lhs);
558   MEM_freeN(rhs);
559   return ret;
560 }
561
562 void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
563 {
564   RNA_pointer_create(NULL, ot->srna, NULL, ptr);
565 }
566
567 void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
568 {
569   wmOperatorType *ot = WM_operatortype_find(opstring, false);
570
571   if (ot) {
572     WM_operator_properties_create_ptr(ptr, ot);
573   }
574   else {
575     RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
576   }
577 }
578
579 /* similar to the function above except its uses ID properties
580  * used for keymaps and macros */
581 void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
582 {
583   if (*properties == NULL) {
584     IDPropertyTemplate val = {0};
585     *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
586   }
587
588   if (*ptr == NULL) {
589     *ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
590     WM_operator_properties_create(*ptr, opstring);
591   }
592
593   (*ptr)->data = *properties;
594 }
595
596 void WM_operator_properties_sanitize(PointerRNA *ptr, const bool no_context)
597 {
598   RNA_STRUCT_BEGIN (ptr, prop) {
599     switch (RNA_property_type(prop)) {
600       case PROP_ENUM:
601         if (no_context) {
602           RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
603         }
604         else {
605           RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
606         }
607         break;
608       case PROP_POINTER: {
609         StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
610
611         /* recurse into operator properties */
612         if (RNA_struct_is_a(ptype, &RNA_OperatorProperties)) {
613           PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
614           WM_operator_properties_sanitize(&opptr, no_context);
615         }
616         break;
617       }
618       default:
619         break;
620     }
621   }
622   RNA_STRUCT_END;
623 }
624
625 /** set all props to their default,
626  * \param do_update: Only update un-initialized props.
627  *
628  * \note, there's nothing specific to operators here.
629  * this could be made a general function.
630  */
631 bool WM_operator_properties_default(PointerRNA *ptr, const bool do_update)
632 {
633   bool changed = false;
634   RNA_STRUCT_BEGIN (ptr, prop) {
635     switch (RNA_property_type(prop)) {
636       case PROP_POINTER: {
637         StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
638         if (ptype != &RNA_Struct) {
639           PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
640           changed |= WM_operator_properties_default(&opptr, do_update);
641         }
642         break;
643       }
644       default:
645         if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) {
646           if (RNA_property_reset(ptr, prop, -1)) {
647             changed = true;
648           }
649         }
650         break;
651     }
652   }
653   RNA_STRUCT_END;
654
655   return changed;
656 }
657
658 /* remove all props without PROP_SKIP_SAVE */
659 void WM_operator_properties_reset(wmOperator *op)
660 {
661   if (op->ptr->data) {
662     PropertyRNA *iterprop;
663     iterprop = RNA_struct_iterator_property(op->type->srna);
664
665     RNA_PROP_BEGIN (op->ptr, itemptr, iterprop) {
666       PropertyRNA *prop = itemptr.data;
667
668       if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
669         const char *identifier = RNA_property_identifier(prop);
670         RNA_struct_idprops_unset(op->ptr, identifier);
671       }
672     }
673     RNA_PROP_END;
674   }
675 }
676
677 void WM_operator_properties_clear(PointerRNA *ptr)
678 {
679   IDProperty *properties = ptr->data;
680
681   if (properties) {
682     IDP_ClearProperty(properties);
683   }
684 }
685
686 void WM_operator_properties_free(PointerRNA *ptr)
687 {
688   IDProperty *properties = ptr->data;
689
690   if (properties) {
691     IDP_FreeProperty(properties);
692     MEM_freeN(properties);
693     ptr->data = NULL; /* just in case */
694   }
695 }
696
697 /* ************ default op callbacks, exported *********** */
698
699 void WM_operator_view3d_unit_defaults(struct bContext *C, struct wmOperator *op)
700 {
701   if (op->flag & OP_IS_INVOKE) {
702     Scene *scene = CTX_data_scene(C);
703     View3D *v3d = CTX_wm_view3d(C);
704
705     const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) :
706                             ED_scene_grid_scale(scene, NULL);
707
708     /* always run, so the values are initialized,
709      * otherwise we may get differ behavior when (dia != 1.0) */
710     RNA_STRUCT_BEGIN (op->ptr, prop) {
711       if (RNA_property_type(prop) == PROP_FLOAT) {
712         PropertySubType pstype = RNA_property_subtype(prop);
713         if (pstype == PROP_DISTANCE) {
714           /* we don't support arrays yet */
715           BLI_assert(RNA_property_array_check(prop) == false);
716           /* initialize */
717           if (!RNA_property_is_set_ex(op->ptr, prop, false)) {
718             const float value = RNA_property_float_get_default(op->ptr, prop) * dia;
719             RNA_property_float_set(op->ptr, prop, value);
720           }
721         }
722       }
723     }
724     RNA_STRUCT_END;
725   }
726 }
727
728 int WM_operator_smooth_viewtx_get(const wmOperator *op)
729 {
730   return (op->flag & OP_IS_INVOKE) ? U.smooth_viewtx : 0;
731 }
732
733 /* invoke callback, uses enum property named "type" */
734 int WM_menu_invoke_ex(bContext *C, wmOperator *op, int opcontext)
735 {
736   PropertyRNA *prop = op->type->prop;
737   uiPopupMenu *pup;
738   uiLayout *layout;
739
740   if (prop == NULL) {
741     CLOG_ERROR(WM_LOG_OPERATORS, "'%s' has no enum property set", op->type->idname);
742   }
743   else if (RNA_property_type(prop) != PROP_ENUM) {
744     CLOG_ERROR(WM_LOG_OPERATORS,
745                "'%s', '%s' is not an enum property",
746                op->type->idname,
747                RNA_property_identifier(prop));
748   }
749   else if (RNA_property_is_set(op->ptr, prop)) {
750     const int retval = op->type->exec(C, op);
751     OPERATOR_RETVAL_CHECK(retval);
752     return retval;
753   }
754   else {
755     pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
756     layout = UI_popup_menu_layout(pup);
757     /* set this so the default execution context is the same as submenus */
758     uiLayoutSetOperatorContext(layout, opcontext);
759     uiItemsFullEnumO(
760         layout, op->type->idname, RNA_property_identifier(prop), op->ptr->data, opcontext, 0);
761     UI_popup_menu_end(C, pup);
762     return OPERATOR_INTERFACE;
763   }
764
765   return OPERATOR_CANCELLED;
766 }
767
768 int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
769 {
770   return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_REGION_WIN);
771 }
772
773 struct EnumSearchMenu {
774   wmOperator *op; /* the operator that will be executed when selecting an item */
775
776   bool use_previews;
777   short prv_cols, prv_rows;
778 };
779
780 /* generic enum search invoke popup */
781 static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg)
782 {
783   struct EnumSearchMenu *search_menu = arg;
784   wmWindow *win = CTX_wm_window(C);
785   wmOperator *op = search_menu->op;
786   /* template_ID uses 4 * widget_unit for width, we use a bit more, some items may have a suffix to show */
787   const int width = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_cols :
788                                                 UI_searchbox_size_x();
789   const int height = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_rows :
790                                                  UI_searchbox_size_y();
791   static char search[256] = "";
792   uiBlock *block;
793   uiBut *but;
794
795   block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
796   UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
797   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
798
799   search[0] = '\0';
800   BLI_assert(search_menu->use_previews ||
801              (search_menu->prv_cols == 0 && search_menu->prv_rows == 0));
802 #if 0 /* ok, this isn't so easy... */
803   uiDefBut(block,
804            UI_BTYPE_LABEL,
805            0,
806            RNA_struct_ui_name(op->type->srna),
807            10,
808            10,
809            UI_searchbox_size_x(),
810            UI_UNIT_Y,
811            NULL,
812            0.0,
813            0.0,
814            0,
815            0,
816            "");
817 #endif
818   but = uiDefSearchButO_ptr(block,
819                             op->type,
820                             op->ptr->data,
821                             search,
822                             0,
823                             ICON_VIEWZOOM,
824                             sizeof(search),
825                             10,
826                             10,
827                             width,
828                             UI_UNIT_Y,
829                             search_menu->prv_rows,
830                             search_menu->prv_cols,
831                             "");
832
833   /* fake button, it holds space for search items */
834   uiDefBut(block,
835            UI_BTYPE_LABEL,
836            0,
837            "",
838            10,
839            10 - UI_searchbox_size_y(),
840            width,
841            height,
842            NULL,
843            0,
844            0,
845            0,
846            0,
847            NULL);
848
849   /* Move it downwards, mouse over button. */
850   UI_block_bounds_set_popup(block, 6, (const int[2]){0, -UI_UNIT_Y});
851
852   UI_but_focus_on_enter_event(win, but);
853
854   return block;
855 }
856
857 /**
858  * Similar to #WM_enum_search_invoke, but draws previews. Also, this can't
859  * be used as invoke callback directly since it needs additional info.
860  */
861 int WM_enum_search_invoke_previews(bContext *C, wmOperator *op, short prv_cols, short prv_rows)
862 {
863   static struct EnumSearchMenu search_menu;
864
865   search_menu.op = op;
866   search_menu.use_previews = true;
867   search_menu.prv_cols = prv_cols;
868   search_menu.prv_rows = prv_rows;
869
870   UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu);
871
872   return OPERATOR_INTERFACE;
873 }
874
875 int WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
876 {
877   static struct EnumSearchMenu search_menu;
878   search_menu.op = op;
879   UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu);
880   return OPERATOR_INTERFACE;
881 }
882
883 /* Can't be used as an invoke directly, needs message arg (can be NULL) */
884 int WM_operator_confirm_message_ex(
885     bContext *C, wmOperator *op, const char *title, const int icon, const char *message)
886 {
887   uiPopupMenu *pup;
888   uiLayout *layout;
889   IDProperty *properties = op->ptr->data;
890
891   if (properties && properties->len) {
892     properties = IDP_CopyProperty(op->ptr->data);
893   }
894   else {
895     properties = NULL;
896   }
897
898   pup = UI_popup_menu_begin(C, title, icon);
899   layout = UI_popup_menu_layout(pup);
900   uiItemFullO_ptr(
901       layout, op->type, message, ICON_NONE, properties, WM_OP_EXEC_REGION_WIN, 0, NULL);
902   UI_popup_menu_end(C, pup);
903
904   return OPERATOR_INTERFACE;
905 }
906
907 int WM_operator_confirm_message(bContext *C, wmOperator *op, const char *message)
908 {
909   return WM_operator_confirm_message_ex(C, op, IFACE_("OK?"), ICON_QUESTION, message);
910 }
911
912 int WM_operator_confirm(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
913 {
914   return WM_operator_confirm_message(C, op, NULL);
915 }
916
917 int WM_operator_confirm_or_exec(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
918 {
919   const bool confirm = RNA_boolean_get(op->ptr, "confirm");
920   if (confirm) {
921     return WM_operator_confirm_message(C, op, NULL);
922   }
923   else {
924     return op->type->exec(C, op);
925   }
926 }
927
928 /* op->invoke, opens fileselect if path property not set, otherwise executes */
929 int WM_operator_filesel(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
930 {
931   if (RNA_struct_property_is_set(op->ptr, "filepath")) {
932     return WM_operator_call_notest(C, op); /* call exec direct */
933   }
934   else {
935     WM_event_add_fileselect(C, op);
936     return OPERATOR_RUNNING_MODAL;
937   }
938 }
939
940 bool WM_operator_filesel_ensure_ext_imtype(wmOperator *op, const struct ImageFormatData *im_format)
941 {
942   PropertyRNA *prop;
943   char filepath[FILE_MAX];
944   /* dont NULL check prop, this can only run on ops with a 'filepath' */
945   prop = RNA_struct_find_property(op->ptr, "filepath");
946   RNA_property_string_get(op->ptr, prop, filepath);
947   if (BKE_image_path_ensure_ext_from_imformat(filepath, im_format)) {
948     RNA_property_string_set(op->ptr, prop, filepath);
949     /* note, we could check for and update 'filename' here,
950      * but so far nothing needs this. */
951     return true;
952   }
953   return false;
954 }
955
956 /* op->poll */
957 bool WM_operator_winactive(bContext *C)
958 {
959   if (CTX_wm_window(C) == NULL) {
960     return 0;
961   }
962   return 1;
963 }
964
965 /* return false, if the UI should be disabled */
966 bool WM_operator_check_ui_enabled(const bContext *C, const char *idname)
967 {
968   wmWindowManager *wm = CTX_wm_manager(C);
969   Scene *scene = CTX_data_scene(C);
970
971   return !((ED_undo_is_valid(C, idname) == false) || WM_jobs_test(wm, scene, WM_JOB_TYPE_ANY));
972 }
973
974 wmOperator *WM_operator_last_redo(const bContext *C)
975 {
976   wmWindowManager *wm = CTX_wm_manager(C);
977   wmOperator *op;
978
979   /* only for operators that are registered and did an undo push */
980   for (op = wm->operators.last; op; op = op->prev) {
981     if ((op->type->flag & OPTYPE_REGISTER) && (op->type->flag & OPTYPE_UNDO)) {
982       break;
983     }
984   }
985
986   return op;
987 }
988
989 IDProperty *WM_operator_last_properties_ensure_idprops(wmOperatorType *ot)
990 {
991   if (ot->last_properties == NULL) {
992     IDPropertyTemplate val = {0};
993     ot->last_properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
994   }
995   return ot->last_properties;
996 }
997
998 void WM_operator_last_properties_ensure(wmOperatorType *ot, PointerRNA *ptr)
999 {
1000   IDProperty *props = WM_operator_last_properties_ensure_idprops(ot);
1001   RNA_pointer_create(NULL, ot->srna, props, ptr);
1002 }
1003
1004 /**
1005  * Use for drag & drop a path or name with operators invoke() function.
1006  */
1007 ID *WM_operator_drop_load_path(struct bContext *C, wmOperator *op, const short idcode)
1008 {
1009   Main *bmain = CTX_data_main(C);
1010   ID *id = NULL;
1011   /* check input variables */
1012   if (RNA_struct_property_is_set(op->ptr, "filepath")) {
1013     const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path");
1014     char path[FILE_MAX];
1015     bool exists = false;
1016
1017     RNA_string_get(op->ptr, "filepath", path);
1018
1019     errno = 0;
1020
1021     if (idcode == ID_IM) {
1022       id = (ID *)BKE_image_load_exists_ex(bmain, path, &exists);
1023     }
1024     else {
1025       BLI_assert(0);
1026     }
1027
1028     if (!id) {
1029       BKE_reportf(op->reports,
1030                   RPT_ERROR,
1031                   "Cannot read %s '%s': %s",
1032                   BKE_idcode_to_name(idcode),
1033                   path,
1034                   errno ? strerror(errno) : TIP_("unsupported format"));
1035       return NULL;
1036     }
1037
1038     if (is_relative_path) {
1039       if (exists == false) {
1040         if (idcode == ID_IM) {
1041           BLI_path_rel(((Image *)id)->name, BKE_main_blendfile_path(bmain));
1042         }
1043         else {
1044           BLI_assert(0);
1045         }
1046       }
1047     }
1048   }
1049   else if (RNA_struct_property_is_set(op->ptr, "name")) {
1050     char name[MAX_ID_NAME - 2];
1051     RNA_string_get(op->ptr, "name", name);
1052     id = BKE_libblock_find_name(bmain, idcode, name);
1053     if (!id) {
1054       BKE_reportf(op->reports, RPT_ERROR, "%s '%s' not found", BKE_idcode_to_name(idcode), name);
1055       return NULL;
1056     }
1057     id_us_plus(id);
1058   }
1059
1060   return id;
1061 }
1062
1063 static void wm_block_redo_cb(bContext *C, void *arg_op, int UNUSED(arg_event))
1064 {
1065   wmOperator *op = arg_op;
1066
1067   if (op == WM_operator_last_redo(C)) {
1068     /* operator was already executed once? undo & repeat */
1069     ED_undo_operator_repeat(C, op);
1070   }
1071   else {
1072     /* operator not executed yet, call it */
1073     ED_undo_push_op(C, op);
1074     wm_operator_register(C, op);
1075
1076     WM_operator_repeat(C, op);
1077   }
1078 }
1079
1080 static void wm_block_redo_cancel_cb(bContext *C, void *arg_op)
1081 {
1082   wmOperator *op = arg_op;
1083
1084   /* if operator never got executed, free it */
1085   if (op != WM_operator_last_redo(C)) {
1086     WM_operator_free(op);
1087   }
1088 }
1089
1090 static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op)
1091 {
1092   wmOperator *op = arg_op;
1093   uiBlock *block;
1094   uiLayout *layout;
1095   uiStyle *style = UI_style_get();
1096   int width = 15 * UI_UNIT_X;
1097
1098   block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
1099   UI_block_flag_disable(block, UI_BLOCK_LOOP);
1100   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_REGULAR);
1101
1102   /* UI_BLOCK_NUMSELECT for layer buttons */
1103   UI_block_flag_enable(block, UI_BLOCK_NUMSELECT | UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
1104
1105   /* if register is not enabled, the operator gets freed on OPERATOR_FINISHED
1106    * ui_apply_but_funcs_after calls ED_undo_operator_repeate_cb and crashes */
1107   assert(op->type->flag & OPTYPE_REGISTER);
1108
1109   UI_block_func_handle_set(block, wm_block_redo_cb, arg_op);
1110   layout = UI_block_layout(
1111       block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, width, UI_UNIT_Y, 0, style);
1112
1113   if (op == WM_operator_last_redo(C)) {
1114     if (!WM_operator_check_ui_enabled(C, op->type->name)) {
1115       uiLayoutSetEnabled(layout, false);
1116     }
1117   }
1118
1119   if (op->type->flag & OPTYPE_MACRO) {
1120     for (op = op->macro.first; op; op = op->next) {
1121       uiTemplateOperatorPropertyButs(
1122           C, layout, op, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN, UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
1123       if (op->next) {
1124         uiItemS(layout);
1125       }
1126     }
1127   }
1128   else {
1129     uiTemplateOperatorPropertyButs(
1130         C, layout, op, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN, UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
1131   }
1132
1133   UI_block_bounds_set_popup(block, 4, NULL);
1134
1135   return block;
1136 }
1137
1138 typedef struct wmOpPopUp {
1139   wmOperator *op;
1140   int width;
1141   int height;
1142   int free_op;
1143 } wmOpPopUp;
1144
1145 /* Only invoked by OK button in popups created with wm_block_dialog_create() */
1146 static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
1147 {
1148   wmOpPopUp *data = arg1;
1149   uiBlock *block = arg2;
1150
1151   /* Explicitly set UI_RETURN_OK flag, otherwise the menu might be canceled
1152    * in case WM_operator_call_ex exits/reloads the current file (T49199). */
1153   UI_popup_menu_retval_set(block, UI_RETURN_OK, true);
1154
1155   WM_operator_call_ex(C, data->op, true);
1156
1157   /* let execute handle freeing it */
1158   //data->free_op = false;
1159   //data->op = NULL;
1160
1161   /* in this case, wm_operator_ui_popup_cancel wont run */
1162   MEM_freeN(data);
1163
1164   /* get context data *after* WM_operator_call_ex which might have closed the current file and changed context */
1165   wmWindowManager *wm = CTX_wm_manager(C);
1166   wmWindow *win = CTX_wm_window(C);
1167
1168   /* check window before 'block->handle' incase the
1169    * popup execution closed the window and freed the block. see T44688.
1170    */
1171   /* Post 2.78 TODO: Check if this fix and others related to T44688 are still
1172    * needed or can be improved now that requesting context data has been corrected
1173    * (see above). We're close to release so not a good time for experiments.
1174    * -- Julian
1175    */
1176   if (BLI_findindex(&wm->windows, win) != -1) {
1177     UI_popup_block_close(C, win, block);
1178   }
1179 }
1180
1181 /* Dialogs are popups that require user verification (click OK) before exec */
1182 static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData)
1183 {
1184   wmOpPopUp *data = userData;
1185   wmOperator *op = data->op;
1186   uiBlock *block;
1187   uiLayout *layout;
1188   uiStyle *style = UI_style_get();
1189
1190   block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
1191   UI_block_flag_disable(block, UI_BLOCK_LOOP);
1192   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_REGULAR);
1193
1194   /* intentionally don't use 'UI_BLOCK_MOVEMOUSE_QUIT', some dialogues have many items
1195    * where quitting by accident is very annoying */
1196   UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_NUMSELECT);
1197
1198   layout = UI_block_layout(
1199       block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style);
1200
1201   uiTemplateOperatorPropertyButs(
1202       C, layout, op, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN, UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
1203
1204   /* clear so the OK button is left alone */
1205   UI_block_func_set(block, NULL, NULL, NULL);
1206
1207   /* new column so as not to interfere with custom layouts [#26436] */
1208   {
1209     uiBlock *col_block;
1210     uiLayout *col;
1211     uiBut *btn;
1212
1213     col = uiLayoutColumn(layout, false);
1214     col_block = uiLayoutGetBlock(col);
1215     /* Create OK button, the callback of which will execute op */
1216     btn = uiDefBut(
1217         col_block, UI_BTYPE_BUT, 0, IFACE_("OK"), 0, -30, 0, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
1218     UI_but_flag_enable(btn, UI_BUT_ACTIVE_DEFAULT);
1219     UI_but_func_set(btn, dialog_exec_cb, data, col_block);
1220   }
1221
1222   /* center around the mouse */
1223   UI_block_bounds_set_popup(block, 4, (const int[2]){data->width / -2, data->height / 2});
1224
1225   UI_block_active_only_flagged_buttons(C, ar, block);
1226
1227   return block;
1228 }
1229
1230 static uiBlock *wm_operator_ui_create(bContext *C, ARegion *ar, void *userData)
1231 {
1232   wmOpPopUp *data = userData;
1233   wmOperator *op = data->op;
1234   uiBlock *block;
1235   uiLayout *layout;
1236   uiStyle *style = UI_style_get();
1237
1238   block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
1239   UI_block_flag_disable(block, UI_BLOCK_LOOP);
1240   UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
1241   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_REGULAR);
1242
1243   layout = UI_block_layout(
1244       block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style);
1245
1246   /* since ui is defined the auto-layout args are not used */
1247   uiTemplateOperatorPropertyButs(C, layout, op, UI_BUT_LABEL_ALIGN_COLUMN, 0);
1248
1249   UI_block_func_set(block, NULL, NULL, NULL);
1250
1251   UI_block_bounds_set_popup(block, 4, NULL);
1252
1253   UI_block_active_only_flagged_buttons(C, ar, block);
1254
1255   return block;
1256 }
1257
1258 static void wm_operator_ui_popup_cancel(struct bContext *C, void *userData)
1259 {
1260   wmOpPopUp *data = userData;
1261   wmOperator *op = data->op;
1262
1263   if (op) {
1264     if (op->type->cancel) {
1265       op->type->cancel(C, op);
1266     }
1267
1268     if (data->free_op) {
1269       WM_operator_free(op);
1270     }
1271   }
1272
1273   MEM_freeN(data);
1274 }
1275
1276 static void wm_operator_ui_popup_ok(struct bContext *C, void *arg, int retval)
1277 {
1278   wmOpPopUp *data = arg;
1279   wmOperator *op = data->op;
1280
1281   if (op && retval > 0) {
1282     WM_operator_call_ex(C, op, true);
1283   }
1284
1285   MEM_freeN(data);
1286 }
1287
1288 int WM_operator_ui_popup(bContext *C, wmOperator *op, int width, int height)
1289 {
1290   wmOpPopUp *data = MEM_callocN(sizeof(wmOpPopUp), "WM_operator_ui_popup");
1291   data->op = op;
1292   data->width = width * U.dpi_fac;
1293   data->height = height * U.dpi_fac;
1294   data->free_op = true; /* if this runs and gets registered we may want not to free it */
1295   UI_popup_block_ex(C, wm_operator_ui_create, NULL, wm_operator_ui_popup_cancel, data, op);
1296   return OPERATOR_RUNNING_MODAL;
1297 }
1298
1299 /**
1300  * For use by #WM_operator_props_popup_call, #WM_operator_props_popup only.
1301  *
1302  * \note operator menu needs undo flag enabled, for redo callback */
1303 static int wm_operator_props_popup_ex(bContext *C,
1304                                       wmOperator *op,
1305                                       const bool do_call,
1306                                       const bool do_redo)
1307 {
1308   if ((op->type->flag & OPTYPE_REGISTER) == 0) {
1309     BKE_reportf(op->reports,
1310                 RPT_ERROR,
1311                 "Operator '%s' does not have register enabled, incorrect invoke function",
1312                 op->type->idname);
1313     return OPERATOR_CANCELLED;
1314   }
1315
1316   if (do_redo) {
1317     if ((op->type->flag & OPTYPE_UNDO) == 0) {
1318       BKE_reportf(op->reports,
1319                   RPT_ERROR,
1320                   "Operator '%s' does not have undo enabled, incorrect invoke function",
1321                   op->type->idname);
1322       return OPERATOR_CANCELLED;
1323     }
1324   }
1325
1326   /* if we don't have global undo, we can't do undo push for automatic redo,
1327    * so we require manual OK clicking in this popup */
1328   if (!do_redo || !(U.uiflag & USER_GLOBALUNDO)) {
1329     return WM_operator_props_dialog_popup(C, op, 300, 20);
1330   }
1331
1332   UI_popup_block_ex(C, wm_block_create_redo, NULL, wm_block_redo_cancel_cb, op, op);
1333
1334   if (do_call) {
1335     wm_block_redo_cb(C, op, 0);
1336   }
1337
1338   return OPERATOR_RUNNING_MODAL;
1339 }
1340
1341 /**
1342  * Same as #WM_operator_props_popup but don't use operator redo.
1343  * just wraps #WM_operator_props_dialog_popup.
1344  */
1345 int WM_operator_props_popup_confirm(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1346 {
1347   return wm_operator_props_popup_ex(C, op, false, false);
1348 }
1349
1350 /**
1351  * Same as #WM_operator_props_popup but call the operator first,
1352  * This way - the button values correspond to the result of the operator.
1353  * Without this, first access to a button will make the result jump, see T32452.
1354  */
1355 int WM_operator_props_popup_call(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1356 {
1357   return wm_operator_props_popup_ex(C, op, true, true);
1358 }
1359
1360 int WM_operator_props_popup(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1361 {
1362   return wm_operator_props_popup_ex(C, op, false, true);
1363 }
1364
1365 int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, int height)
1366 {
1367   wmOpPopUp *data = MEM_callocN(sizeof(wmOpPopUp), "WM_operator_props_dialog_popup");
1368
1369   data->op = op;
1370   data->width = width * U.dpi_fac;
1371   data->height = height * U.dpi_fac;
1372   data->free_op = true; /* if this runs and gets registered we may want not to free it */
1373
1374   /* op is not executed until popup OK but is clicked */
1375   UI_popup_block_ex(
1376       C, wm_block_dialog_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data, op);
1377
1378   return OPERATOR_RUNNING_MODAL;
1379 }
1380
1381 int WM_operator_redo_popup(bContext *C, wmOperator *op)
1382 {
1383   /* CTX_wm_reports(C) because operator is on stack, not active in event system */
1384   if ((op->type->flag & OPTYPE_REGISTER) == 0) {
1385     BKE_reportf(CTX_wm_reports(C),
1386                 RPT_ERROR,
1387                 "Operator redo '%s' does not have register enabled, incorrect invoke function",
1388                 op->type->idname);
1389     return OPERATOR_CANCELLED;
1390   }
1391   if (op->type->poll && op->type->poll(C) == 0) {
1392     BKE_reportf(
1393         CTX_wm_reports(C), RPT_ERROR, "Operator redo '%s': wrong context", op->type->idname);
1394     return OPERATOR_CANCELLED;
1395   }
1396
1397   UI_popup_block_invoke(C, wm_block_create_redo, op);
1398
1399   return OPERATOR_CANCELLED;
1400 }
1401
1402 /* ***************** Debug menu ************************* */
1403
1404 static int wm_debug_menu_exec(bContext *C, wmOperator *op)
1405 {
1406   G.debug_value = RNA_int_get(op->ptr, "debug_value");
1407   ED_screen_refresh(CTX_wm_manager(C), CTX_wm_window(C));
1408   WM_event_add_notifier(C, NC_WINDOW, NULL);
1409
1410   return OPERATOR_FINISHED;
1411 }
1412
1413 static int wm_debug_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1414 {
1415   RNA_int_set(op->ptr, "debug_value", G.debug_value);
1416   return WM_operator_props_dialog_popup(C, op, 180, 20);
1417 }
1418
1419 static void WM_OT_debug_menu(wmOperatorType *ot)
1420 {
1421   ot->name = "Debug Menu";
1422   ot->idname = "WM_OT_debug_menu";
1423   ot->description = "Open a popup to set the debug level";
1424
1425   ot->invoke = wm_debug_menu_invoke;
1426   ot->exec = wm_debug_menu_exec;
1427   ot->poll = WM_operator_winactive;
1428
1429   RNA_def_int(ot->srna, "debug_value", 0, SHRT_MIN, SHRT_MAX, "Debug Value", "", -10000, 10000);
1430 }
1431
1432 /* ***************** Operator defaults ************************* */
1433 static int wm_operator_defaults_exec(bContext *C, wmOperator *op)
1434 {
1435   PointerRNA ptr = CTX_data_pointer_get_type(C, "active_operator", &RNA_Operator);
1436
1437   if (!ptr.data) {
1438     BKE_report(op->reports, RPT_ERROR, "No operator in context");
1439     return OPERATOR_CANCELLED;
1440   }
1441
1442   WM_operator_properties_reset((wmOperator *)ptr.data);
1443   return OPERATOR_FINISHED;
1444 }
1445
1446 /* used by operator preset menu. pre-2.65 this was a 'Reset' button */
1447 static void WM_OT_operator_defaults(wmOperatorType *ot)
1448 {
1449   ot->name = "Restore Defaults";
1450   ot->idname = "WM_OT_operator_defaults";
1451   ot->description = "Set the active operator to its default values";
1452
1453   ot->exec = wm_operator_defaults_exec;
1454
1455   ot->flag = OPTYPE_INTERNAL;
1456 }
1457
1458 /* ***************** Splash Screen ************************* */
1459
1460 static void wm_block_splash_close(bContext *C, void *arg_block, void *UNUSED(arg))
1461 {
1462   wmWindow *win = CTX_wm_window(C);
1463   UI_popup_block_close(C, win, arg_block);
1464 }
1465
1466 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *arg_unused);
1467
1468 static void wm_block_splash_refreshmenu(bContext *C, void *UNUSED(arg_block), void *UNUSED(arg))
1469 {
1470   ARegion *ar_menu = CTX_wm_menu(C);
1471   ED_region_tag_refresh_ui(ar_menu);
1472 }
1473
1474 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(arg))
1475 {
1476   uiBlock *block;
1477   uiBut *but;
1478   uiStyle *style = UI_style_get();
1479
1480 #ifndef WITH_HEADLESS
1481   extern char datatoc_splash_png[];
1482   extern int datatoc_splash_png_size;
1483
1484   extern char datatoc_splash_2x_png[];
1485   extern int datatoc_splash_2x_png_size;
1486   ImBuf *ibuf;
1487 #else
1488   ImBuf *ibuf = NULL;
1489 #endif
1490
1491 #ifdef WITH_BUILDINFO
1492   int label_delta = 0;
1493   int hash_width, date_width;
1494   char date_buf[128] = "\0";
1495   char hash_buf[128] = "\0";
1496   extern unsigned long build_commit_timestamp;
1497   extern char build_hash[], build_commit_date[], build_commit_time[], build_branch[];
1498
1499   /* Builds made from tag only shows tag sha */
1500   BLI_snprintf(hash_buf, sizeof(hash_buf), "Hash: %s", build_hash);
1501   BLI_snprintf(date_buf, sizeof(date_buf), "Date: %s %s", build_commit_date, build_commit_time);
1502
1503   BLF_size(style->widgetlabel.uifont_id, style->widgetlabel.points, U.pixelsize * U.dpi);
1504   hash_width = (int)BLF_width(style->widgetlabel.uifont_id, hash_buf, sizeof(hash_buf)) +
1505                U.widget_unit;
1506   date_width = (int)BLF_width(style->widgetlabel.uifont_id, date_buf, sizeof(date_buf)) +
1507                U.widget_unit;
1508 #endif /* WITH_BUILDINFO */
1509
1510 #ifndef WITH_HEADLESS
1511   if (U.dpi_fac > 1.0) {
1512     ibuf = IMB_ibImageFromMemory((const uchar *)datatoc_splash_2x_png,
1513                                  datatoc_splash_2x_png_size,
1514                                  IB_rect,
1515                                  NULL,
1516                                  "<splash screen>");
1517   }
1518   else {
1519     ibuf = IMB_ibImageFromMemory((const uchar *)datatoc_splash_png,
1520                                  datatoc_splash_png_size,
1521                                  IB_rect,
1522                                  NULL,
1523                                  "<splash screen>");
1524   }
1525
1526   /* overwrite splash with template image */
1527   if (U.app_template[0] != '\0') {
1528     ImBuf *ibuf_template = NULL;
1529     char splash_filepath[FILE_MAX];
1530     char template_directory[FILE_MAX];
1531
1532     if (BKE_appdir_app_template_id_search(
1533             U.app_template, template_directory, sizeof(template_directory))) {
1534       BLI_join_dirfile(splash_filepath,
1535                        sizeof(splash_filepath),
1536                        template_directory,
1537                        (U.pixelsize == 2) ? "splash_2x.png" : "splash.png");
1538       ibuf_template = IMB_loadiffname(splash_filepath, IB_rect, NULL);
1539       if (ibuf_template) {
1540         const int x_expect = ibuf->x;
1541         const int y_expect = 250 * (int)U.dpi_fac;
1542         /* don't cover the header text */
1543         if (ibuf_template->x == x_expect && ibuf_template->y == y_expect) {
1544           memcpy(ibuf->rect,
1545                  ibuf_template->rect,
1546                  ibuf_template->x * ibuf_template->y * sizeof(char[4]));
1547         }
1548         else {
1549           CLOG_ERROR(WM_LOG_OPERATORS,
1550                      "Splash expected %dx%d found %dx%d, ignoring: %s\n",
1551                      x_expect,
1552                      y_expect,
1553                      ibuf_template->x,
1554                      ibuf_template->y,
1555                      splash_filepath);
1556         }
1557         IMB_freeImBuf(ibuf_template);
1558       }
1559     }
1560   }
1561 #endif
1562
1563   block = UI_block_begin(C, ar, "splash", UI_EMBOSS);
1564
1565   /* note on UI_BLOCK_NO_WIN_CLIP, the window size is not always synchronized
1566    * with the OS when the splash shows, window clipping in this case gives
1567    * ugly results and clipping the splash isn't useful anyway, just disable it [#32938] */
1568   UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_KEEP_OPEN | UI_BLOCK_NO_WIN_CLIP);
1569   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
1570
1571   but = uiDefBut(block,
1572                  UI_BTYPE_IMAGE,
1573                  0,
1574                  "",
1575                  0,
1576                  0.5f * U.widget_unit,
1577                  U.dpi_fac * 501,
1578                  U.dpi_fac * 250,
1579                  ibuf,
1580                  0.0,
1581                  0.0,
1582                  0,
1583                  0,
1584                  ""); /* button owns the imbuf now */
1585   UI_but_func_set(but, wm_block_splash_close, block, NULL);
1586   UI_block_func_set(block, wm_block_splash_refreshmenu, block, NULL);
1587
1588   /* label for 'a' bugfix releases, or 'Release Candidate 1'...
1589    * avoids recreating splash for version updates */
1590   const char *version_suffix = NULL;
1591
1592   if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha")) {
1593     version_suffix = " Alpha";
1594   }
1595   else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "beta")) {
1596     version_suffix = " Beta";
1597   }
1598   else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) {
1599     version_suffix = " Release Candidate";
1600   }
1601   else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) {
1602     version_suffix = STRINGIFY(BLENDER_VERSION_CHAR);
1603   }
1604
1605   char *version = BLI_sprintfN(
1606       "Version %d.%d%s", BLENDER_VERSION / 100, BLENDER_VERSION % 100, version_suffix);
1607
1608   if (version != NULL && version[0]) {
1609     /* placed after the version number in the image,
1610      * placing y is tricky to match baseline */
1611     /* hack to have text draw 'text_sel' */
1612     UI_block_emboss_set(block, UI_EMBOSS_NONE);
1613     int x = 202 * U.dpi_fac;
1614     int y = 130 * U.dpi_fac;
1615     int w = 240 * U.dpi_fac;
1616
1617     but = uiDefBut(block, UI_BTYPE_LABEL, 0, version, x, y, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
1618     /* XXX, set internal flag - UI_SELECT */
1619     UI_but_flag_enable(but, 1);
1620     UI_block_emboss_set(block, UI_EMBOSS);
1621   }
1622
1623   MEM_freeN(version);
1624
1625 #ifdef WITH_BUILDINFO
1626   if (build_commit_timestamp != 0) {
1627     but = uiDefBut(block,
1628                    UI_BTYPE_LABEL,
1629                    0,
1630                    date_buf,
1631                    U.dpi_fac * 502 - date_width,
1632                    U.dpi_fac * 237,
1633                    date_width,
1634                    UI_UNIT_Y,
1635                    NULL,
1636                    0,
1637                    0,
1638                    0,
1639                    0,
1640                    NULL);
1641     /* XXX, set internal flag - UI_SELECT */
1642     UI_but_flag_enable(but, 0);
1643     label_delta = 12;
1644   }
1645   but = uiDefBut(block,
1646                  UI_BTYPE_LABEL,
1647                  0,
1648                  hash_buf,
1649                  U.dpi_fac * 502 - hash_width,
1650                  U.dpi_fac * (237 - label_delta),
1651                  hash_width,
1652                  UI_UNIT_Y,
1653                  NULL,
1654                  0,
1655                  0,
1656                  0,
1657                  0,
1658                  NULL);
1659   /* XXX, set internal flag - UI_SELECT */
1660   UI_but_flag_enable(but, 0);
1661
1662   if (!STREQ(build_branch, "master")) {
1663     char branch_buf[128] = "\0";
1664     int branch_width;
1665     BLI_snprintf(branch_buf, sizeof(branch_buf), "Branch: %s", build_branch);
1666     branch_width = (int)BLF_width(style->widgetlabel.uifont_id, branch_buf, sizeof(branch_buf)) +
1667                    U.widget_unit;
1668     but = uiDefBut(block,
1669                    UI_BTYPE_LABEL,
1670                    0,
1671                    branch_buf,
1672                    U.dpi_fac * 502 - branch_width,
1673                    U.dpi_fac * (225 - label_delta),
1674                    branch_width,
1675                    UI_UNIT_Y,
1676                    NULL,
1677                    0,
1678                    0,
1679                    0,
1680                    0,
1681                    NULL);
1682     /* XXX, set internal flag - UI_SELECT */
1683     UI_but_flag_enable(but, 0);
1684   }
1685 #endif /* WITH_BUILDINFO */
1686
1687   uiLayout *layout = UI_block_layout(block,
1688                                      UI_LAYOUT_VERTICAL,
1689                                      UI_LAYOUT_PANEL,
1690                                      U.dpi_fac * 40,
1691                                      0,
1692                                      U.dpi_fac * 450,
1693                                      U.dpi_fac * 110,
1694                                      0,
1695                                      style);
1696
1697   MenuType *mt = WM_menutype_find("WM_MT_splash", true);
1698   if (mt) {
1699     UI_menutype_draw(C, mt, layout);
1700   }
1701
1702   UI_block_bounds_set_centered(block, 0);
1703
1704   return block;
1705 }
1706
1707 static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
1708 {
1709   UI_popup_block_invoke(C, wm_block_create_splash, NULL);
1710
1711   return OPERATOR_FINISHED;
1712 }
1713
1714 static void WM_OT_splash(wmOperatorType *ot)
1715 {
1716   ot->name = "Splash Screen";
1717   ot->idname = "WM_OT_splash";
1718   ot->description = "Open the splash screen with release info";
1719
1720   ot->invoke = wm_splash_invoke;
1721   ot->poll = WM_operator_winactive;
1722 }
1723
1724 /* ***************** Search menu ************************* */
1725
1726 struct SearchPopupInit_Data {
1727   int size[2];
1728 };
1729
1730 static uiBlock *wm_block_search_menu(bContext *C, ARegion *ar, void *userdata)
1731 {
1732   const struct SearchPopupInit_Data *init_data = userdata;
1733   static char search[256] = "";
1734   uiBlock *block;
1735   uiBut *but;
1736
1737   block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
1738   UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
1739   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
1740
1741   but = uiDefSearchBut(block,
1742                        search,
1743                        0,
1744                        ICON_VIEWZOOM,
1745                        sizeof(search),
1746                        10,
1747                        10,
1748                        init_data->size[0],
1749                        UI_UNIT_Y,
1750                        0,
1751                        0,
1752                        "");
1753   UI_but_func_operator_search(but);
1754   UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
1755
1756   /* fake button, it holds space for search items */
1757   uiDefBut(block,
1758            UI_BTYPE_LABEL,
1759            0,
1760            "",
1761            10,
1762            10 - init_data->size[1],
1763            init_data->size[0],
1764            init_data->size[1],
1765            NULL,
1766            0,
1767            0,
1768            0,
1769            0,
1770            NULL);
1771
1772   /* Move it downwards, mouse over button. */
1773   UI_block_bounds_set_popup(block, 6, (const int[2]){0, -UI_UNIT_Y});
1774
1775   return block;
1776 }
1777
1778 static int wm_search_menu_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
1779 {
1780   return OPERATOR_FINISHED;
1781 }
1782
1783 static int wm_search_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1784 {
1785   /* Exception for launching via spacebar */
1786   if (event->type == SPACEKEY) {
1787     bool ok = true;
1788     ScrArea *sa = CTX_wm_area(C);
1789     if (sa) {
1790       if (sa->spacetype == SPACE_CONSOLE) {
1791         /* So we can use the shortcut in the console. */
1792         ok = false;
1793       }
1794       else if (sa->spacetype == SPACE_TEXT) {
1795         /* So we can use the spacebar in the text editor. */
1796         ok = false;
1797       }
1798     }
1799     else {
1800       Object *editob = CTX_data_edit_object(C);
1801       if (editob && editob->type == OB_FONT) {
1802         /* So we can use the spacebar for entering text. */
1803         ok = false;
1804       }
1805     }
1806     if (!ok) {
1807       return OPERATOR_PASS_THROUGH;
1808     }
1809   }
1810
1811   static struct SearchPopupInit_Data data;
1812   data.size[0] = UI_searchbox_size_x() * 2;
1813   data.size[1] = UI_searchbox_size_y();
1814
1815   UI_popup_block_invoke(C, wm_block_search_menu, &data);
1816
1817   return OPERATOR_INTERFACE;
1818 }
1819
1820 static void WM_OT_search_menu(wmOperatorType *ot)
1821 {
1822   ot->name = "Search Menu";
1823   ot->idname = "WM_OT_search_menu";
1824   ot->description = "Pop-up a search menu over all available operators in current context";
1825
1826   ot->invoke = wm_search_menu_invoke;
1827   ot->exec = wm_search_menu_exec;
1828   ot->poll = WM_operator_winactive;
1829 }
1830
1831 static int wm_call_menu_exec(bContext *C, wmOperator *op)
1832 {
1833   char idname[BKE_ST_MAXNAME];
1834   RNA_string_get(op->ptr, "name", idname);
1835
1836   return UI_popup_menu_invoke(C, idname, op->reports);
1837 }
1838
1839 static void WM_OT_call_menu(wmOperatorType *ot)
1840 {
1841   ot->name = "Call Menu";
1842   ot->idname = "WM_OT_call_menu";
1843   ot->description = "Call (draw) a pre-defined menu";
1844
1845   ot->exec = wm_call_menu_exec;
1846   ot->poll = WM_operator_winactive;
1847
1848   ot->flag = OPTYPE_INTERNAL;
1849
1850   RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu");
1851 }
1852
1853 static int wm_call_pie_menu_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1854 {
1855   char idname[BKE_ST_MAXNAME];
1856   RNA_string_get(op->ptr, "name", idname);
1857
1858   return UI_pie_menu_invoke(C, idname, event);
1859 }
1860
1861 static int wm_call_pie_menu_exec(bContext *C, wmOperator *op)
1862 {
1863   char idname[BKE_ST_MAXNAME];
1864   RNA_string_get(op->ptr, "name", idname);
1865
1866   return UI_pie_menu_invoke(C, idname, CTX_wm_window(C)->eventstate);
1867 }
1868
1869 static void WM_OT_call_menu_pie(wmOperatorType *ot)
1870 {
1871   ot->name = "Call Pie Menu";
1872   ot->idname = "WM_OT_call_menu_pie";
1873   ot->description = "Call (draw) a pre-defined pie menu";
1874
1875   ot->invoke = wm_call_pie_menu_invoke;
1876   ot->exec = wm_call_pie_menu_exec;
1877   ot->poll = WM_operator_winactive;
1878
1879   ot->flag = OPTYPE_INTERNAL;
1880
1881   RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu");
1882 }
1883
1884 static int wm_call_panel_exec(bContext *C, wmOperator *op)
1885 {
1886   char idname[BKE_ST_MAXNAME];
1887   RNA_string_get(op->ptr, "name", idname);
1888   const bool keep_open = RNA_boolean_get(op->ptr, "keep_open");
1889
1890   return UI_popover_panel_invoke(C, idname, keep_open, op->reports);
1891 }
1892
1893 static void WM_OT_call_panel(wmOperatorType *ot)
1894 {
1895   ot->name = "Call Panel";
1896   ot->idname = "WM_OT_call_panel";
1897   ot->description = "Call (draw) a pre-defined panel";
1898
1899   ot->exec = wm_call_panel_exec;
1900   ot->poll = WM_operator_winactive;
1901
1902   ot->flag = OPTYPE_INTERNAL;
1903
1904   PropertyRNA *prop;
1905
1906   prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu");
1907   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1908   prop = RNA_def_boolean(ot->srna, "keep_open", true, "Keep Open", "");
1909   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1910 }
1911
1912 /* ************ window / screen operator definitions ************** */
1913
1914 /* this poll functions is needed in place of WM_operator_winactive
1915  * while it crashes on full screen */
1916 static bool wm_operator_winactive_normal(bContext *C)
1917 {
1918   wmWindow *win = CTX_wm_window(C);
1919   bScreen *screen;
1920
1921   if (win == NULL) {
1922     return 0;
1923   }
1924   if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL))) {
1925     return 0;
1926   }
1927
1928   return 1;
1929 }
1930
1931 /* included for script-access */
1932 static void WM_OT_window_close(wmOperatorType *ot)
1933 {
1934   ot->name = "Close Window";
1935   ot->idname = "WM_OT_window_close";
1936   ot->description = "Close the current window";
1937
1938   ot->exec = wm_window_close_exec;
1939   ot->poll = WM_operator_winactive;
1940 }
1941
1942 static void WM_OT_window_new(wmOperatorType *ot)
1943 {
1944   ot->name = "New Window";
1945   ot->idname = "WM_OT_window_new";
1946   ot->description = "Create a new window";
1947
1948   ot->exec = wm_window_new_exec;
1949   ot->poll = wm_operator_winactive_normal;
1950 }
1951
1952 static void WM_OT_window_new_main(wmOperatorType *ot)
1953 {
1954   ot->name = "New Main Window";
1955   ot->idname = "WM_OT_window_new_main";
1956   ot->description = "Create a new main window with its own workspace and scene selection";
1957
1958   ot->exec = wm_window_new_main_exec;
1959   ot->poll = wm_operator_winactive_normal;
1960 }
1961
1962 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
1963 {
1964   ot->name = "Toggle Window Fullscreen";
1965   ot->idname = "WM_OT_window_fullscreen_toggle";
1966   ot->description = "Toggle the current window fullscreen";
1967
1968   ot->exec = wm_window_fullscreen_toggle_exec;
1969   ot->poll = WM_operator_winactive;
1970 }
1971
1972 static int wm_exit_blender_exec(bContext *C, wmOperator *UNUSED(op))
1973 {
1974   wm_exit_schedule_delayed(C);
1975   return OPERATOR_FINISHED;
1976 }
1977
1978 static int wm_exit_blender_invoke(bContext *C,
1979                                   wmOperator *UNUSED(op),
1980                                   const wmEvent *UNUSED(event))
1981 {
1982   if (U.uiflag & USER_SAVE_PROMPT) {
1983     wm_quit_with_optional_confirmation_prompt(C, CTX_wm_window(C));
1984   }
1985   else {
1986     wm_exit_schedule_delayed(C);
1987   }
1988   return OPERATOR_FINISHED;
1989 }
1990
1991 static void WM_OT_quit_blender(wmOperatorType *ot)
1992 {
1993   ot->name = "Quit Blender";
1994   ot->idname = "WM_OT_quit_blender";
1995   ot->description = "Quit Blender";
1996
1997   ot->invoke = wm_exit_blender_invoke;
1998   ot->exec = wm_exit_blender_exec;
1999 }
2000
2001 /* *********************** */
2002
2003 #if defined(WIN32)
2004
2005 static int wm_console_toggle_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
2006 {
2007   GHOST_toggleConsole(2);
2008   return OPERATOR_FINISHED;
2009 }
2010
2011 static void WM_OT_console_toggle(wmOperatorType *ot)
2012 {
2013   /* XXX Have to mark these for xgettext, as under linux they do not exists... */
2014   ot->name = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Toggle System Console");
2015   ot->idname = "WM_OT_console_toggle";
2016   ot->description = N_("Toggle System Console");
2017
2018   ot->exec = wm_console_toggle_exec;
2019   ot->poll = WM_operator_winactive;
2020 }
2021
2022 #endif
2023
2024 /* ************ default paint cursors, draw always around cursor *********** */
2025 /*
2026  * - returns handler to free
2027  * - poll(bContext): returns 1 if draw should happen
2028  * - draw(bContext): drawing callback for paint cursor
2029  */
2030
2031 wmPaintCursor *WM_paint_cursor_activate(wmWindowManager *wm,
2032                                         short space_type,
2033                                         short region_type,
2034                                         bool (*poll)(bContext *C),
2035                                         wmPaintCursorDraw draw,
2036                                         void *customdata)
2037 {
2038   wmPaintCursor *pc = MEM_callocN(sizeof(wmPaintCursor), "paint cursor");
2039
2040   BLI_addtail(&wm->paintcursors, pc);
2041
2042   pc->customdata = customdata;
2043   pc->poll = poll;
2044   pc->draw = draw;
2045
2046   pc->space_type = space_type;
2047   pc->region_type = region_type;
2048
2049   return pc;
2050 }
2051
2052 bool WM_paint_cursor_end(wmWindowManager *wm, wmPaintCursor *handle)
2053 {
2054   wmPaintCursor *pc;
2055
2056   for (pc = wm->paintcursors.first; pc; pc = pc->next) {
2057     if (pc == (wmPaintCursor *)handle) {
2058       BLI_remlink(&wm->paintcursors, pc);
2059       MEM_freeN(pc);
2060       return true;
2061     }
2062   }
2063   return false;
2064 }
2065
2066 /* *********************** radial control ****************** */
2067
2068 #define WM_RADIAL_CONTROL_DISPLAY_SIZE (200 * UI_DPI_FAC)
2069 #define WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE (35 * UI_DPI_FAC)
2070 #define WM_RADIAL_CONTROL_DISPLAY_WIDTH \
2071   (WM_RADIAL_CONTROL_DISPLAY_SIZE - WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE)
2072 #define WM_RADIAL_MAX_STR 10
2073
2074 typedef struct {
2075   PropertyType type;
2076   PropertySubType subtype;
2077   PointerRNA ptr, col_ptr, fill_col_ptr, rot_ptr, zoom_ptr, image_id_ptr;
2078   PointerRNA fill_col_override_ptr, fill_col_override_test_ptr;
2079   PropertyRNA *prop, *col_prop, *fill_col_prop, *rot_prop, *zoom_prop;
2080   PropertyRNA *fill_col_override_prop, *fill_col_override_test_prop;
2081   StructRNA *image_id_srna;
2082   float initial_value, current_value, min_value, max_value;
2083   int initial_mouse[2];
2084   int slow_mouse[2];
2085   bool slow_mode;
2086   Dial *dial;
2087   unsigned int gltex;
2088   ListBase orig_paintcursors;
2089   bool use_secondary_tex;
2090   void *cursor;
2091   NumInput num_input;
2092 } RadialControl;
2093
2094 static void radial_control_update_header(wmOperator *op, bContext *C)
2095 {
2096   RadialControl *rc = op->customdata;
2097   char msg[UI_MAX_DRAW_STR];
2098   ScrArea *sa = CTX_wm_area(C);
2099   Scene *scene = CTX_data_scene(C);
2100
2101   if (hasNumInput(&rc->num_input)) {
2102     char num_str[NUM_STR_REP_LEN];
2103     outputNumInput(&rc->num_input, num_str, &scene->unit);
2104     BLI_snprintf(msg, sizeof(msg), "%s: %s", RNA_property_ui_name(rc->prop), num_str);
2105   }
2106   else {
2107     const char *ui_name = RNA_property_ui_name(rc->prop);
2108     switch (rc->subtype) {
2109       case PROP_NONE:
2110       case PROP_DISTANCE:
2111         BLI_snprintf(msg, sizeof(msg), "%s: %0.4f", ui_name, rc->current_value);
2112         break;
2113       case PROP_PIXEL:
2114         BLI_snprintf(msg,
2115                      sizeof(msg),
2116                      "%s: %d",
2117                      ui_name,
2118                      (int)rc->current_value); /* XXX: round to nearest? */
2119         break;
2120       case PROP_PERCENTAGE:
2121         BLI_snprintf(msg, sizeof(msg), "%s: %3.1f%%", ui_name, rc->current_value);
2122         break;
2123       case PROP_FACTOR:
2124         BLI_snprintf(msg, sizeof(msg), "%s: %1.3f", ui_name, rc->current_value);
2125         break;
2126       case PROP_ANGLE:
2127         BLI_snprintf(msg, sizeof(msg), "%s: %3.2f", ui_name, RAD2DEGF(rc->current_value));
2128         break;
2129       default:
2130         BLI_snprintf(msg, sizeof(msg), "%s", ui_name); /* XXX: No value? */
2131         break;
2132     }
2133   }
2134
2135   ED_area_status_text(sa, msg);
2136 }
2137
2138 static void radial_control_set_initial_mouse(RadialControl *rc, const wmEvent *event)
2139 {
2140   float d[2] = {0, 0};
2141   float zoom[2] = {1, 1};
2142
2143   rc->initial_mouse[0] = event->x;
2144   rc->initial_mouse[1] = event->y;
2145
2146   switch (rc->subtype) {
2147     case PROP_NONE:
2148     case PROP_DISTANCE:
2149     case PROP_PIXEL:
2150       d[0] = rc->initial_value;
2151       break;
2152     case PROP_PERCENTAGE:
2153       d[0] = (rc->initial_value) / 100.0f * WM_RADIAL_CONTROL_DISPLAY_WIDTH +
2154              WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2155       break;
2156     case PROP_FACTOR:
2157       d[0] = (1 - rc->initial_value) * WM_RADIAL_CONTROL_DISPLAY_WIDTH +
2158              WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2159       break;
2160     case PROP_ANGLE:
2161       d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * cosf(rc->initial_value);
2162       d[1] = WM_RADIAL_CONTROL_DISPLAY_SIZE * sinf(rc->initial_value);
2163       break;
2164     default:
2165       return;
2166   }
2167
2168   if (rc->zoom_prop) {
2169     RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2170     d[0] *= zoom[0];
2171     d[1] *= zoom[1];
2172   }
2173
2174   rc->initial_mouse[0] -= d[0];
2175   rc->initial_mouse[1] -= d[1];
2176 }
2177
2178 static void radial_control_set_tex(RadialControl *rc)
2179 {
2180   ImBuf *ibuf;
2181
2182   switch (RNA_type_to_ID_code(rc->image_id_ptr.type)) {
2183     case ID_BR:
2184       if ((ibuf = BKE_brush_gen_radial_control_imbuf(rc->image_id_ptr.data,
2185                                                      rc->use_secondary_tex))) {
2186         glGenTextures(1, &rc->gltex);
2187         glBindTexture(GL_TEXTURE_2D, rc->gltex);
2188         glTexImage2D(
2189             GL_TEXTURE_2D, 0, GL_R8, ibuf->x, ibuf->y, 0, GL_RED, GL_FLOAT, ibuf->rect_float);
2190         glBindTexture(GL_TEXTURE_2D, 0);
2191         MEM_freeN(ibuf->rect_float);
2192         MEM_freeN(ibuf);
2193       }
2194       break;
2195     default:
2196       break;
2197   }
2198 }
2199
2200 static void radial_control_paint_tex(RadialControl *rc, float radius, float alpha)
2201 {
2202   float col[3] = {0, 0, 0};
2203   float rot;
2204
2205   /* set fill color */
2206   if (rc->fill_col_prop) {
2207     PointerRNA *fill_ptr;
2208     PropertyRNA *fill_prop;
2209
2210     if (rc->fill_col_override_prop && RNA_property_boolean_get(&rc->fill_col_override_test_ptr,
2211                                                                rc->fill_col_override_test_prop)) {
2212       fill_ptr = &rc->fill_col_override_ptr;
2213       fill_prop = rc->fill_col_override_prop;
2214     }
2215     else {
2216       fill_ptr = &rc->fill_col_ptr;
2217       fill_prop = rc->fill_col_prop;
2218     }
2219
2220     RNA_property_float_get_array(fill_ptr, fill_prop, col);
2221   }
2222
2223   GPUVertFormat *format = immVertexFormat();
2224   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2225
2226   if (rc->gltex) {
2227
2228     uint texCoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2229
2230     glActiveTexture(GL_TEXTURE0);
2231     glBindTexture(GL_TEXTURE_2D, rc->gltex);
2232
2233     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2234     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2235
2236     GLint swizzleMask[] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED};
2237     glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
2238
2239     /* set up rotation if available */
2240     if (rc->rot_prop) {
2241       rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop);
2242       GPU_matrix_push();
2243       GPU_matrix_rotate_2d(RAD2DEGF(rot));
2244     }
2245
2246     immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR);
2247
2248     immUniformColor3fvAlpha(col, alpha);
2249     immUniform1i("image", 0);
2250
2251     /* draw textured quad */
2252     immBegin(GPU_PRIM_TRI_FAN, 4);
2253
2254     immAttr2f(texCoord, 0, 0);
2255     immVertex2f(pos, -radius, -radius);
2256
2257     immAttr2f(texCoord, 1, 0);
2258     immVertex2f(pos, radius, -radius);
2259
2260     immAttr2f(texCoord, 1, 1);
2261     immVertex2f(pos, radius, radius);
2262
2263     immAttr2f(texCoord, 0, 1);
2264     immVertex2f(pos, -radius, radius);
2265
2266     immEnd();
2267
2268     /* undo rotation */
2269     if (rc->rot_prop) {
2270       GPU_matrix_pop();
2271     }
2272   }
2273   else {
2274     /* flat color if no texture available */
2275     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2276     immUniformColor3fvAlpha(col, alpha);
2277     imm_draw_circle_fill_2d(pos, 0.0f, 0.0f, radius, 40);
2278   }
2279
2280   immUnbindProgram();
2281 }
2282
2283 static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void *customdata)
2284 {
2285   RadialControl *rc = customdata;
2286   uiStyle *style = UI_style_get();
2287   const uiFontStyle *fstyle = &style->widget;
2288   const int fontid = fstyle->uifont_id;
2289   short fstyle_points = fstyle->points;
2290   char str[WM_RADIAL_MAX_STR];
2291   short strdrawlen = 0;
2292   float strwidth, strheight;
2293   float r1 = 0.0f, r2 = 0.0f, rmin = 0.0, tex_radius, alpha;
2294   float zoom[2], col[3] = {1, 1, 1};
2295
2296   switch (rc->subtype) {
2297     case PROP_NONE:
2298     case PROP_DISTANCE:
2299     case PROP_PIXEL:
2300       r1 = rc->current_value;
2301       r2 = rc->initial_value;
2302       tex_radius = r1;
2303       alpha = 0.75;
2304       break;
2305     case PROP_PERCENTAGE:
2306       r1 = rc->current_value / 100.0f * WM_RADIAL_CONTROL_DISPLAY_WIDTH +
2307            WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2308       r2 = tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE;
2309       rmin = WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2310       BLI_snprintf(str, WM_RADIAL_MAX_STR, "%3.1f%%", rc->current_value);
2311       strdrawlen = BLI_strlen_utf8(str);
2312       tex_radius = r1;
2313       alpha = 0.75;
2314       break;
2315     case PROP_FACTOR:
2316       r1 = (1 - rc->current_value) * WM_RADIAL_CONTROL_DISPLAY_WIDTH +
2317            WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2318       r2 = tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE;
2319       rmin = WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2320       alpha = rc->current_value / 2.0f + 0.5f;
2321       BLI_snprintf(str, WM_RADIAL_MAX_STR, "%1.3f", rc->current_value);
2322       strdrawlen = BLI_strlen_utf8(str);
2323       break;
2324     case PROP_ANGLE:
2325       r1 = r2 = tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE;
2326       alpha = 0.75;
2327       rmin = WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2328       BLI_snprintf(str, WM_RADIAL_MAX_STR, "%3.2f", RAD2DEGF(rc->current_value));
2329       strdrawlen = BLI_strlen_utf8(str);
2330       break;
2331     default:
2332       tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE; /* note, this is a dummy value */
2333       alpha = 0.75;
2334       break;
2335   }
2336
2337   /* Keep cursor in the original place */
2338   x = rc->initial_mouse[0];
2339   y = rc->initial_mouse[1];
2340   GPU_matrix_translate_2f((float)x, (float)y);
2341
2342   GPU_blend(true);
2343   GPU_line_smooth(true);
2344
2345   /* apply zoom if available */
2346   if (rc->zoom_prop) {
2347     RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2348     GPU_matrix_scale_2fv(zoom);
2349   }
2350
2351   /* draw rotated texture */
2352   radial_control_paint_tex(rc, tex_radius, alpha);
2353
2354   /* set line color */
2355   if (rc->col_prop) {
2356     RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col);
2357   }
2358
2359   GPUVertFormat *format = immVertexFormat();
2360   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2361
2362   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2363   immUniformColor3fvAlpha(col, 0.5f);
2364
2365   if (rc->subtype == PROP_ANGLE) {
2366     GPU_matrix_push();
2367
2368     /* draw original angle line */
2369     GPU_matrix_rotate_3f(RAD2DEGF(rc->initial_value), 0.0f, 0.0f, 1.0f);
2370     immBegin(GPU_PRIM_LINES, 2);
2371     immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f);
2372     immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
2373     immEnd();
2374
2375     /* draw new angle line */
2376     GPU_matrix_rotate_3f(RAD2DEGF(rc->current_value - rc->initial_value), 0.0f, 0.0f, 1.0f);
2377     immBegin(GPU_PRIM_LINES, 2);
2378     immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f);
2379     immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
2380     immEnd();
2381
2382     GPU_matrix_pop();
2383   }
2384
2385   /* draw circles on top */
2386   imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r1, 40);
2387   imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r2, 40);
2388   if (rmin > 0.0f) {
2389     imm_draw_circle_wire_2d(pos, 0.0, 0.0f, rmin, 40);
2390   }
2391   immUnbindProgram();
2392
2393   BLF_size(fontid, 1.5 * fstyle_points * U.pixelsize, U.dpi);
2394   BLF_enable(fontid, BLF_SHADOW);
2395   BLF_shadow(fontid, 3, (const float[4]){0.0f, 0.0f, 0.0f, 0.5f});
2396   BLF_shadow_offset(fontid, 1, -1);
2397
2398   /* draw value */
2399   BLF_width_and_height(fontid, str, strdrawlen, &strwidth, &strheight);
2400   BLF_position(fontid, -0.5f * strwidth, -0.5f * strheight, 0.0f);
2401   BLF_draw(fontid, str, strdrawlen);
2402
2403   BLF_disable(fontid, BLF_SHADOW);
2404
2405   GPU_blend(false);
2406   GPU_line_smooth(false);
2407 }
2408
2409 typedef enum {
2410   RC_PROP_ALLOW_MISSING = 1,
2411   RC_PROP_REQUIRE_FLOAT = 2,
2412   RC_PROP_REQUIRE_BOOL = 4,
2413 } RCPropFlags;
2414
2415 /**
2416  * Attempt to retrieve the rna pointer/property from an rna path.
2417  *
2418  * \return 0 for failure, 1 for success, and also 1 if property is not set.
2419  */
2420 static int radial_control_get_path(PointerRNA *ctx_ptr,
2421                                    wmOperator *op,
2422                                    const char *name,
2423                                    PointerRNA *r_ptr,
2424                                    PropertyRNA **r_prop,
2425                                    int req_length,
2426                                    RCPropFlags flags)
2427 {
2428   PropertyRNA *unused_prop;
2429   int len;
2430   char *str;
2431
2432   /* check flags */
2433   if ((flags & RC_PROP_REQUIRE_BOOL) && (flags & RC_PROP_REQUIRE_FLOAT)) {
2434     BKE_report(op->reports, RPT_ERROR, "Property cannot be both boolean and float");
2435     return 0;
2436   }
2437
2438   /* get an rna string path from the operator's properties */
2439   if (!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0))) {
2440     return 1;
2441   }
2442
2443   if (str[0] == '\0') {
2444     if (r_prop) {
2445       *r_prop = NULL;
2446     }
2447     MEM_freeN(str);
2448     return 1;
2449   }
2450
2451   if (!r_prop) {
2452     r_prop = &unused_prop;
2453   }
2454
2455   /* get rna from path */
2456   if (!RNA_path_resolve(ctx_ptr, str, r_ptr, r_prop)) {
2457     MEM_freeN(str);
2458     if (flags & RC_PROP_ALLOW_MISSING) {
2459       return 1;
2460     }
2461     else {
2462       BKE_reportf(op->reports, RPT_ERROR, "Could not resolve path '%s'", name);
2463       return 0;
2464     }
2465   }
2466
2467   /* check property type */
2468   if (flags & (RC_PROP_REQUIRE_BOOL | RC_PROP_REQUIRE_FLOAT)) {
2469     PropertyType prop_type = RNA_property_type(*r_prop);
2470
2471     if (((flags & RC_PROP_REQUIRE_BOOL) && (prop_type != PROP_BOOLEAN)) ||
2472         ((flags & RC_PROP_REQUIRE_FLOAT) && (prop_type != PROP_FLOAT))) {
2473       MEM_freeN(str);
2474       BKE_reportf(op->reports, RPT_ERROR, "Property from path '%s' is not a float", name);
2475       return 0;
2476     }
2477   }
2478
2479   /* check property's array length */
2480   if (*r_prop && (len = RNA_property_array_length(r_ptr, *r_prop)) != req_length) {
2481     MEM_freeN(str);
2482     BKE_reportf(op->reports,
2483                 RPT_ERROR,
2484                 "Property from path '%s' has length %d instead of %d",
2485                 name,
2486                 len,
2487                 req_length);
2488     return 0;
2489   }
2490
2491   /* success */
2492   MEM_freeN(str);
2493   return 1;
2494 }
2495
2496 /* initialize the rna pointers and properties using rna paths */
2497 static int radial_control_get_properties(bContext *C, wmOperator *op)
2498 {
2499   RadialControl *rc = op->customdata;
2500   PointerRNA ctx_ptr, use_secondary_ptr;
2501   PropertyRNA *use_secondary_prop = NULL;
2502   const char *data_path;
2503
2504   RNA_pointer_create(NULL, &RNA_Context, C, &ctx_ptr);
2505
2506   /* check if we use primary or secondary path */
2507   if (!radial_control_get_path(&ctx_ptr,
2508                                op,
2509                                "use_secondary",
2510                                &use_secondary_ptr,
2511                                &use_secondary_prop,
2512                                0,
2513                                (RC_PROP_ALLOW_MISSING | RC_PROP_REQUIRE_BOOL))) {
2514     return 0;
2515   }
2516   else {
2517     if (use_secondary_prop && RNA_property_boolean_get(&use_secondary_ptr, use_secondary_prop)) {
2518       data_path = "data_path_secondary";
2519     }
2520     else {
2521       data_path = "data_path_primary";
2522     }
2523   }
2524
2525   if (!radial_control_get_path(&ctx_ptr, op, data_path, &rc->ptr, &rc->prop, 0, 0)) {
2526     return 0;
2527   }
2528
2529   /* data path is required */
2530   if (!rc->prop) {
2531     return 0;
2532   }
2533
2534   if (!radial_control_get_path(
2535           &ctx_ptr, op, "rotation_path", &rc->rot_ptr, &rc->rot_prop, 0, RC_PROP_REQUIRE_FLOAT)) {
2536     return 0;
2537   }
2538
2539   if (!radial_control_get_path(
2540           &ctx_ptr, op, "color_path", &rc->col_ptr, &rc->col_prop, 3, RC_PROP_REQUIRE_FLOAT)) {
2541     return 0;
2542   }
2543
2544   if (!radial_control_get_path(&ctx_ptr,
2545                                op,
2546                                "fill_color_path",
2547                                &rc->fill_col_ptr,
2548                                &rc->fill_col_prop,
2549                                3,
2550                                RC_PROP_REQUIRE_FLOAT)) {
2551     return 0;
2552   }
2553
2554   if (!radial_control_get_path(&ctx_ptr,
2555                                op,
2556                                "fill_color_override_path",
2557                                &rc->fill_col_override_ptr,
2558                                &rc->fill_col_override_prop,
2559                                3,
2560                                RC_PROP_REQUIRE_FLOAT)) {
2561     return 0;
2562   }
2563   if (!radial_control_get_path(&ctx_ptr,
2564                                op,
2565                                "fill_color_override_test_path",
2566                                &rc->fill_col_override_test_ptr,
2567                                &rc->fill_col_override_test_prop,
2568                                0,
2569                                RC_PROP_REQUIRE_BOOL)) {
2570     return 0;
2571   }
2572
2573   /* slightly ugly; allow this property to not resolve
2574    * correctly. needed because 3d texture paint shares the same
2575    * keymap as 2d image paint */
2576   if (!radial_control_get_path(&ctx_ptr,
2577                                op,
2578                                "zoom_path",
2579                                &rc->zoom_ptr,
2580                                &rc->zoom_prop,
2581                                2,
2582                                RC_PROP_REQUIRE_FLOAT | RC_PROP_ALLOW_MISSING)) {
2583     return 0;
2584   }
2585
2586   if (!radial_control_get_path(&ctx_ptr, op, "image_id", &rc->image_id_ptr, NULL, 0, 0)) {
2587     return 0;
2588   }
2589   else if (rc->image_id_ptr.data) {
2590     /* extra check, pointer must be to an ID */
2591     if (!RNA_struct_is_ID(rc->image_id_ptr.type)) {
2592       BKE_report(op->reports, RPT_ERROR, "Pointer from path image_id is not an ID");
2593       return 0;
2594     }
2595   }
2596
2597   rc->use_secondary_tex = RNA_boolean_get(op->ptr, "secondary_tex");
2598
2599   return 1;
2600 }
2601
2602 static int radial_control_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2603 {
2604   wmWindowManager *wm;
2605   RadialControl *rc;
2606
2607   if (!(op->customdata = rc = MEM_callocN(sizeof(RadialControl), "RadialControl"))) {
2608     return OPERATOR_CANCELLED;
2609   }
2610
2611   if (!radial_control_get_properties(C, op)) {
2612     MEM_freeN(rc);
2613     return OPERATOR_CANCELLED;
2614   }
2615
2616   /* get type, initial, min, and max values of the property */
2617   switch ((rc->type = RNA_property_type(rc->prop))) {
2618     case PROP_INT: {
2619       int value, min, max, step;
2620
2621       value = RNA_property_int_get(&rc->ptr, rc->prop);
2622       RNA_property_int_ui_range(&rc->ptr, rc->prop, &min, &max, &step);
2623
2624       rc->initial_value = value;
2625       rc->min_value = min_ii(value, min);
2626       rc->max_value = max_ii(value, max);
2627       break;
2628     }
2629     case PROP_FLOAT: {
2630       float value, min, max, step, precision;
2631
2632       value = RNA_property_float_get(&rc->ptr, rc->prop);
2633       RNA_property_float_ui_range(&rc->ptr, rc->prop, &min, &max, &step, &precision);
2634
2635       rc->initial_value = value;
2636       rc->min_value = min_ff(value, min);
2637       rc->max_value = max_ff(value, max);
2638       break;
2639     }
2640     default:
2641       BKE_report(op->reports, RPT_ERROR, "Property must be an integer or a float");
2642       MEM_freeN(rc);
2643       return OPERATOR_CANCELLED;
2644   }
2645
2646   /* initialize numerical input */
2647   initNumInput(&rc->num_input);
2648   rc->num_input.idx_max = 0;
2649   rc->num_input.val_flag[0] |= NUM_NO_NEGATIVE;
2650   rc->num_input.unit_sys = USER_UNIT_NONE;
2651   rc->num_input.unit_type[0] = B_UNIT_LENGTH;
2652
2653   /* get subtype of property */
2654   rc->subtype = RNA_property_subtype(rc->prop);
2655   if (!ELEM(rc->subtype,
2656             PROP_NONE,
2657             PROP_DISTANCE,
2658             PROP_FACTOR,
2659             PROP_PERCENTAGE,
2660             PROP_ANGLE,
2661             PROP_PIXEL)) {
2662     BKE_report(op->reports,
2663                RPT_ERROR,
2664                "Property must be a none, distance, factor, percentage, angle, or pixel");
2665     MEM_freeN(rc);
2666     return OPERATOR_CANCELLED;
2667   }
2668
2669   rc->current_value = rc->initial_value;
2670   radial_control_set_initial_mouse(rc, event);
2671   radial_control_set_tex(rc);
2672
2673   /* temporarily disable other paint cursors */
2674   wm = CTX_wm_manager(C);
2675   rc->orig_paintcursors = wm->paintcursors;
2676   BLI_listbase_clear(&wm->paintcursors);
2677
2678   /* add radial control paint cursor */
2679   rc->cursor = WM_paint_cursor_activate(
2680       wm, SPACE_TYPE_ANY, RGN_TYPE_ANY, op->type->poll, radial_control_paint_cursor, rc);
2681
2682   WM_event_add_modal_handler(C, op);
2683
2684   return OPERATOR_RUNNING_MODAL;
2685 }
2686
2687 static void radial_control_set_value(RadialControl *rc, float val)
2688 {
2689   switch (rc->type) {
2690     case PROP_INT:
2691       RNA_property_int_set(&rc->ptr, rc->prop, val);
2692       break;
2693     case PROP_FLOAT:
2694       RNA_property_float_set(&rc->ptr, rc->prop, val);
2695       break;
2696     default:
2697       break;
2698   }
2699 }
2700
2701 static void radial_control_cancel(bContext *C, wmOperator *op)
2702 {
2703   RadialControl *rc = op->customdata;
2704   wmWindowManager *wm = CTX_wm_manager(C);
2705   ScrArea *sa = CTX_wm_area(C);
2706
2707   if (rc->dial) {
2708     MEM_freeN(rc->dial);
2709     rc->dial = NULL;
2710   }
2711
2712   ED_area_status_text(sa, NULL);
2713
2714   WM_paint_cursor_end(wm, rc->cursor);
2715
2716   /* restore original paint cursors */
2717   wm->paintcursors = rc->orig_paintcursors;
2718
2719   /* not sure if this is a good notifier to use;
2720    * intended purpose is to update the UI so that the
2721    * new value is displayed in sliders/numfields */
2722   WM_event_add_notifier(C, NC_WINDOW, NULL);
2723
2724   glDeleteTextures(1, &rc->gltex);
2725
2726   MEM_freeN(rc);
2727 }
2728
2729 static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *event)
2730 {
2731   RadialControl *rc = op->customdata;
2732   float new_value, dist = 0.0f, zoom[2];
2733   float delta[2], ret = OPERATOR_RUNNING_MODAL;
2734   bool snap;
2735   float angle_precision = 0.0f;
2736   const bool has_numInput = hasNumInput(&rc->num_input);
2737   bool handled = false;
2738   float numValue;
2739   /* TODO: fix hardcoded events */
2740
2741   snap = event->ctrl != 0;
2742
2743   /* Modal numinput active, try to handle numeric inputs first... */
2744   if (event->val == KM_PRESS && has_numInput && handleNumInput(C, &rc->num_input, event)) {
2745     handled = true;
2746     applyNumInput(&rc->num_input, &numValue);
2747
2748     if (rc->subtype == PROP_ANGLE) {
2749       numValue = DEG2RADF(numValue);
2750       numValue = fmod(numValue, 2.0f * (float)M_PI);
2751       if (numValue < 0.0f) {
2752         numValue += 2.0f * (float)M_PI;
2753       }
2754     }
2755
2756     CLAMP(numValue, rc->min_value, rc->max_value);
2757     new_value = numValue;
2758
2759     radial_control_set_value(rc, new_value);
2760     rc->current_value = new_value;
2761     radial_control_update_header(op, C);
2762     return OPERATOR_RUNNING_MODAL;
2763   }
2764   else {
2765     handled = false;
2766     switch (event->type) {
2767       case ESCKEY:
2768       case RIGHTMOUSE:
2769         /* canceled; restore original value */
2770         radial_control_set_value(rc, rc->initial_value);
2771         ret = OPERATOR_CANCELLED;
2772         break;
2773
2774       case LEFTMOUSE:
2775       case PADENTER:
2776       case RETKEY:
2777         /* done; value already set */
2778         RNA_property_update(C, &rc->ptr, rc->prop);
2779         ret = OPERATOR_FINISHED;
2780         break;
2781
2782       case MOUSEMOVE:
2783         if (!has_numInput) {
2784           if (rc->slow_mode) {
2785             if (rc->subtype == PROP_ANGLE) {
2786               float position[2] = {event->x, event->y};
2787
2788               /* calculate the initial angle here first */
2789               delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0];
2790               delta[1] = rc->initial_mouse[1] - rc->slow_mouse[1];
2791
2792               /* precision angle gets calculated from dial and gets added later */
2793               angle_precision = -0.1f * BLI_dial_angle(rc->dial, position);
2794             }
2795             else {
2796               delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0];
2797               delta[1] = rc->initial_mouse[1] - rc->slow_mouse[1];
2798
2799               if (rc->zoom_prop) {
2800                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2801                 delta[0] /= zoom[0];
2802                 delta[1] /= zoom[1];
2803               }
2804
2805               dist = len_v2(delta);
2806
2807               delta[0] = event->x - rc->slow_mouse[0];
2808               delta[1] = event->y - rc->slow_mouse[1];
2809
2810               if (rc->zoom_prop) {
2811                 delta[0] /= zoom[0];
2812                 delta[1] /= zoom[1];
2813               }
2814
2815               dist = dist + 0.1f * (delta[0] + delta[1]);
2816             }
2817           }
2818           else {
2819             delta[0] = rc->initial_mouse[0] - event->x;
2820             delta[1] = rc->initial_mouse[1] - event->y;
2821
2822             if (rc->zoom_prop) {
2823               RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2824               delta[0] /= zoom[0];
2825               delta[1] /= zoom[1];
2826             }
2827
2828             dist = len_v2(delta);
2829           }
2830
2831           /* calculate new value and apply snapping  */
2832           switch (rc->subtype) {
2833             case PROP_NONE:
2834             case PROP_DISTANCE:
2835             case PROP_PIXEL:
2836               new_value = dist;
2837               if (snap) {
2838                 new_value = ((int)new_value + 5) / 10 * 10;
2839               }
2840               break;
2841             case PROP_PERCENTAGE:
2842               new_value = ((dist - WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE) /
2843                            WM_RADIAL_CONTROL_DISPLAY_WIDTH) *
2844                           100.0f;
2845               if (snap) {
2846                 new_value = ((int)(new_value + 2.5f)) / 5 * 5;
2847               }
2848               break;
2849             case PROP_FACTOR:
2850               new_value = (WM_RADIAL_CONTROL_DISPLAY_SIZE - dist) /
2851                           WM_RADIAL_CONTROL_DISPLAY_WIDTH;
2852               if (snap) {
2853                 new_value = ((int)ceil(new_value * 10.f) * 10.0f) / 100.f;
2854               }
2855               break;
2856             case PROP_ANGLE:
2857               new_value = atan2f(delta[1], delta[0]) + (float)M_PI + angle_precision;
2858               new_value = fmod(new_value, 2.0f * (float)M_PI);
2859               if (new_value < 0.0f) {
2860                 new_value += 2.0f * (float)M_PI;
2861               }
2862               if (snap) {
2863                 new_value = DEG2RADF(((int)RAD2DEGF(new_value) + 5) / 10 * 10);
2864               }
2865               break;
2866             default:
2867               new_value = dist; /* dummy value, should this ever happen? - campbell */
2868               break;
2869           }
2870
2871           /* clamp and update */
2872           CLAMP(new_value, rc->min_value, rc->max_value);
2873           radial_control_set_value(rc, new_value);
2874           rc->current_value = new_value;
2875           handled = true;
2876           break;
2877         }
2878         break;
2879
2880       case LEFTSHIFTKEY:
2881       case RIGHTSHIFTKEY: {
2882         if (event->val == KM_PRESS) {
2883           rc->slow_mouse[0] = event->x;
2884           rc->slow_mouse[1] = event->y;
2885           rc->slow_mode = true;
2886           if (rc->subtype == PROP_ANGLE) {
2887             float initial_position[2] = {UNPACK2(rc->initial_mouse)};
2888             float current_position[2] = {UNPACK2(rc->slow_mouse)};
2889             rc->dial = BLI_dial_initialize(initial_position, 0.0f);
2890             /* immediately set the position to get a an initial direction */
2891             BLI_dial_angle(rc->dial, current_position);
2892           }
2893           handled = true;
2894         }
2895         if (event->val == KM_RELEASE) {
2896           rc->slow_mode = false;
2897           handled = true;
2898           if (rc->dial) {
2899             MEM_freeN(rc->dial);
2900             rc->dial = NULL;
2901           }
2902         }
2903         break;
2904       }
2905     }
2906
2907     /* Modal numinput inactive, try to handle numeric inputs last... */
2908     if (!handled && event->val == KM_PRESS && handleNumInput(C, &rc->num_input, event)) {
2909       applyNumInput(&rc->num_input, &numValue);
2910
2911       if (rc->subtype == PROP_ANGLE) {
2912         numValue = DEG2RADF(numValue);
2913         numValue = fmod(numValue, 2.0f * (float)M_PI);
2914         if (numValue < 0.0f) {
2915           numValue += 2.0f * (float)M_PI;
2916         }
2917       }
2918
2919       CLAMP(numValue, rc->min_value, rc->max_value);
2920       new_value = numValue;
2921
2922       radial_control_set_value(rc, new_value);
2923
2924       rc->current_value = new_value;
2925       radial_control_update_header(op, C);
2926       return OPERATOR_RUNNING_MODAL;
2927     }
2928   }
2929
2930   ED_region_tag_redraw(CTX_wm_region(C));
2931   radial_control_update_header(op, C);
2932
2933   if (ret != OPERATOR_RUNNING_MODAL) {
2934     radial_control_cancel(C, op);
2935   }
2936
2937   return ret;
2938 }
2939
2940 static void WM_OT_radial_control(wmOperatorType *ot)
2941 {
2942   ot->name = "Radial Control";
2943   ot->idname = "WM_OT_radial_control";
2944   ot->description = "Set some size property (like e.g. brush size) with mouse wheel";
2945
2946   ot->invoke = radial_control_invoke;
2947   ot->modal = radial_control_modal;
2948   ot->cancel = radial_control_cancel;
2949
2950   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
2951
2952   /* all paths relative to the context */
2953   PropertyRNA *prop;
2954   prop = RNA_def_string(ot->srna,
2955                         "data_path_primary",
2956                         NULL,
2957                         0,
2958                         "Primary Data Path",
2959                         "Primary path of property to be set by the radial control");
2960   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2961
2962   prop = RNA_def_string(ot->srna,
2963                         "data_path_secondary",
2964                         NULL,
2965                         0,
2966                         "Secondary Data Path",
2967                         "Secondary path of property to be set by the radial control");
2968   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2969
2970   prop = RNA_def_string(ot->srna,
2971                         "use_secondary",
2972                         NULL,
2973                         0,
2974                         "Use Secondary",
2975                         "Path of property to select between the primary and secondary data paths");
2976   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2977
2978   prop = RNA_def_string(ot->srna,
2979                         "rotation_path",
2980                         NULL,
2981                         0,
2982                         "Rotation Path",
2983                         "Path of property used to rotate the texture display");
2984   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2985
2986   prop = RNA_def_string(ot->srna,
2987                         "color_path",
2988                         NULL,
2989                         0,
2990                         "Color Path",
2991                         "Path of property used to set the color of the control");
2992   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2993
2994   prop = RNA_def_string(ot->srna,
2995                         "fill_color_path",
2996                         NULL,
2997                         0,
2998                         "Fill Color Path",
2999                         "Path of property used to set the fill color of the control");
3000   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3001
3002   prop = RNA_def_string(
3003       ot->srna, "fill_color_override_path", NULL, 0, "Fill Color Override Path", "");
3004   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3005   prop = RNA_def_string(
3006       ot->srna, "fill_color_override_test_path", NULL, 0, "Fill Color Override Test", "");
3007   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3008
3009   prop = RNA_def_string(ot->srna,
3010                         "zoom_path",
3011                         NULL,
3012                         0,
3013                         "Zoom Path",
3014                         "Path of property used to set the zoom level for the control");
3015   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3016
3017   prop = RNA_def_string(ot->srna,
3018                         "image_id",
3019                         NULL,
3020                         0,
3021                         "Image ID",
3022                         "Path of ID that is used to generate an image for the control");
3023   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3024
3025   prop = RNA_def_boolean(
3026       ot->srna, "secondary_tex", false, "Secondary Texture", "Tweak brush secondary/mask texture");
3027   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3028 }
3029
3030 /* ************************** timer for testing ***************** */
3031
3032 /* uses no type defines, fully local testing function anyway... ;) */
3033
3034 static void redraw_timer_window_swap(bContext *C)
3035 {
3036   wmWindow *win = CTX_wm_window(C);
3037   ScrArea *sa;
3038   CTX_wm_menu_set(C, NULL);
3039
3040   for (sa = CTX_wm_screen(C)->areabase.first; sa; sa = sa->next) {
3041     ED_area_tag_redraw(sa);
3042   }
3043   wm_draw_update(C);
3044
3045   CTX_wm_window_set(C, win); /* XXX context manipulation warning! */
3046 }
3047
3048 enum {
3049   eRTDrawRegion = 0,
3050   eRTDrawRegionSwap = 1,
3051   eRTDrawWindow = 2,
3052   eRTDrawWindowSwap = 3,
3053   eRTAnimationStep = 4,
3054   eRTAnimationPlay = 5,
3055   eRTUndo = 6,
3056 };
3057
3058 static const EnumPropertyItem redraw_timer_type_items[] = {
3059     {eRTDrawRegion, "DRAW", 0, "Draw Region", "Draw Region"},
3060     {eRTDrawRegionSwap, "DRAW_SWAP", 0, "Draw Region + Swap", "Draw Region and Swap"},
3061     {eRTDrawWindow, "DRAW_WIN", 0, "Draw Window", "Draw Window"},
3062     {eRTDrawWindowSwap, "DRAW_WIN_SWAP", 0, "Draw Window + Swap", "Draw Window and Swap"},
3063     {eRTAnimationStep, "ANIM_STEP", 0, "Anim Step", "Animation Steps"},
3064     {eRTAnimationPlay, "ANIM_PLAY", 0, "Anim Play", "Animation Playback"},
3065     {eRTUndo, "UNDO", 0, "Undo/Redo", "Undo/Redo"},
3066     {0, NULL, 0, NULL, NULL},
3067 };
3068
3069 static void redraw_timer_step(bContext *C,
3070                               Main *bmain,
3071                               Scene *scene,
3072                               struct Depsgraph *depsgraph,
3073                               wmWindow *win,
3074                               ScrArea *sa,
3075                               ARegion *ar,
3076                               const int type,
3077                               const int cfra)
3078 {
3079   if (type == eRTDrawRegion) {
3080     if (ar) {
3081       ED_region_do_draw(C, ar);
3082       ar->do_draw = false;
3083     }
3084   }
3085   else if (type == eRTDrawRegionSwap) {
3086     CTX_wm_menu_set(C, NULL);
3087
3088     ED_region_tag_redraw(ar);
3089     wm_draw_update(C);
3090
3091     CTX_wm_window_set(C, win); /* XXX context manipulation warning! */
3092   }
3093   else if (type == eRTDrawWindow) {
3094     bScreen *screen = WM_window_get_active_screen(win);
3095     ScrArea *sa_iter;
3096
3097     CTX_wm_menu_set(C, NULL);
3098
3099     for (sa_iter = screen->areabase.first; sa_iter; sa_iter = sa_iter->next) {
3100       ARegion *ar_iter;
3101       CTX_wm_area_set(C, sa_iter);
3102
3103       for (ar_iter = sa_iter->regionbase.first; ar_iter; ar_iter = ar_iter->next) {
3104         if (ar_iter->visible) {
3105           CTX_wm_region_set(C, ar_iter);
3106           ED_region_do_draw(C, ar_iter);
3107           ar_iter->do_draw = false;
3108         }
3109       }
3110     }
3111
3112     CTX_wm_window_set(C, win); /* XXX context manipulation warning! */
3113
3114     CTX_wm_area_set(C, sa);
3115     CTX_wm_region_set(C, ar);
3116   }
3117   else if (type == eRTDrawWindowSwap) {
3118     redraw_timer_window_swap(C);
3119   }
3120   else if (type == eRTAnimationStep) {
3121     scene->r.cfra += (cfra == scene->r.cfra) ? 1 : -1;
3122     BKE_scene_graph_update_for_newframe(depsgraph, bmain);
3123   }
3124   else if (type == eRTAnimationPlay) {
3125     /* play anim, return on same frame as started with */
3126     int tot = (scene->r.efra - scene->r.sfra) + 1;
3127
3128     while (tot--) {
3129       /* todo, ability to escape! */
3130       scene->r.cfra++;
3131       if (scene->r.cfra > scene->r.efra) {
3132         scene->r.cfra = scene->r.sfra;
3133       }
3134
3135       BKE_scene_graph_update_for_newframe(depsgraph, bmain);
3136       redraw_timer_window_swap(C);
3137     }
3138   }
3139   else { /* eRTUndo */
3140     ED_undo_pop(C);
3141     ED_undo_redo(C);
3142   }
3143 }
3144
3145 static int redraw_timer_exec(bContext *C, wmOperator *op)
3146 {
3147   Main *bmain = CTX_data_main(C);
3148   Scene *scene = CTX_data_scene(C);
3149   wmWindow *win = CTX_wm_window(C);
3150   ScrArea *sa = CTX_wm_area(C);
3151   ARegion *ar = CTX_wm_region(C);
3152   double time_start, time_delta;
3153   const int type = RNA_enum_get(op->ptr, "type");
3154   const int iter = RNA_int_get(op->ptr, "iterations");
3155   const double time_limit = (double)RNA_float_get(op->ptr, "time_limit");
3156   const int cfra = scene->r.cfra;
3157   int a, iter_steps = 0;
3158   const char *infostr = "";
3159   struct Depsgraph *depsgraph = CTX_data_depsgraph(C);
3160
3161   WM_cursor_wait(1);
3162
3163   time_start = PIL_check_seconds_timer();
3164
3165   for (a = 0; a < iter; a++) {
3166     redraw_timer_step(C, bmain, scene, depsgraph, win, sa, ar, type, cfra);
3167     iter_steps += 1;
3168
3169     if (time_limit != 0.0) {
3170       if ((PIL_check_seconds_timer() - time_start) > time_limit) {
3171         break;
3172       }
3173       a = 0;
3174     }
3175   }
3176
3177   time_delta = (PIL_check_seconds_timer() - time_start) * 1000;
3178
3179   RNA_enum_description(redraw_timer_type_items, type, &infostr);
3180
3181   WM_cursor_wait(0);
3182
3183   BKE_reportf(op->reports,
3184               RPT_WARNING,
3185               "%d x %s: %.4f ms, average: %.8f ms",
3186               iter_steps,
3187               infostr,
3188               time_delta,
3189               time_delta / iter_steps);
3190
3191   return OPERATOR_FINISHED;
3192 }
3193
3194 static void WM_OT_redraw_timer(wmOperatorType *ot)
3195 {
3196   ot->name = "Redraw Timer";
3197   ot->idname = "WM_OT_redraw_timer";
3198   ot->description = "Simple redraw timer to test the speed of updating the interface";
3199
3200   ot->invoke = WM_menu_invoke;
3201   ot->exec = redraw_timer_exec;
3202   ot->poll = WM_operator_winactive;
3203
3204   ot->prop = RNA_def_enum(ot->srna, "type", redraw_timer_type_items, eRTDrawRegion, "Type", "");
3205   RNA_def_int(
3206       ot->srna, "iterations", 10, 1, INT_MAX, "Iterations", "Number of times to redraw", 1, 1000);
3207   RNA_def_float(ot->srna,
3208                 "time_limit",
3209                 0.0,
3210                 0.0,
3211                 FLT_MAX,
3212                 "Time Limit",
3213                 "Seconds to run the test for (override iterations)",
3214                 0.0,
3215                 60.0);
3216 }
3217
3218 /* ************************** memory statistics for testing ***************** */
3219
3220 static int memory_statistics_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
3221 {
3222   MEM_printmemlist_stats();
3223   return OPERATOR_FINISHED;
3224 }
3225
3226 static void WM_OT_memory_statistics(wmOperatorType *ot)
3227 {
3228   ot->name = "Memory Statistics";
3229   ot->idname = "WM_OT_memory_statistics";
3230   ot->description = "Print memory statistics to the console";
3231
3232   ot->exec = memory_statistics_exec;
3233 }
3234
3235 /* *************************** Mat/tex/etc. previews generation ************* */
3236
3237 typedef struct PreviewsIDEnsureData {
3238   bContext *C;
3239   Scene *scene;
3240 } PreviewsIDEnsureData;
3241
3242 static void previews_id_ensure(bContext *C, Scene *scene, ID *id)
3243 {
3244   BLI_assert(ELEM(GS(id->name), ID_MA, ID_TE, ID_IM, ID_WO, ID_LA));
3245
3246   /* Only preview non-library datablocks, lib ones do not pertain to this .blend file!
3247    * Same goes for ID with no user. */
3248   if (!ID_IS_LINKED(id) && (id->us != 0)) {
3249     UI_id_icon_render(C, scene, id, false, false);
3250     UI_id_icon_render(C, scene, id, true, false);
3251   }
3252 }
3253
3254 static int previews_id_ensure_callback(void *userdata,
3255                                        ID *UNUSED(self_id),
3256                                        ID **idptr,
3257                                        int cb_flag)
3258 {
3259   if (cb_flag & IDWALK_CB_PRIVATE) {
3260     return IDWALK_RET_NOP;
3261   }
3262
3263   PreviewsIDEnsureData *data = userdata;
3264   ID *id = *idptr;
3265
3266   if (id && (id->tag & LIB_TAG_DOIT)) {
3267     BLI_assert(ELEM(GS(id->name), ID_MA, ID_TE, ID_IM, ID_WO, ID_LA));
3268     previews_id_ensure(data->C, data->scene, id);
3269     id->tag &= ~LIB_TAG_DOIT;
3270   }
3271
3272   return IDWALK_RET_NOP;
3273 }
3274
3275 static int previews_ensure_exec(bContext *C, wmOperator *UNUSED(op))
3276 {
3277   Main *bmain = CTX_data_main(C);
3278   ListBase *lb[] = {
3279       &bmain->materials, &bmain->textures, &bmain->images, &bmain->worlds, &bmain->lights, NULL};
3280   PreviewsIDEnsureData preview_id_data;
3281   Scene *scene;
3282   ID *id;
3283   int i;
3284
3285   /* We use LIB_TAG_DOIT to check whether we have already handled a given ID or not. */
3286   BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
3287   for (i = 0; lb[i]; i++) {
3288     BKE_main_id_tag_listbase(lb[i], LIB_TAG_DOIT, true);
3289   }
3290
3291   preview_id_data.C = C;
3292   for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
3293     preview_id_data.scene = scene;
3294     id = (ID *)scene;
3295
3296     BKE_library_foreach_ID_link(
3297         NULL, id, previews_id_ensure_callback, &preview_id_data, IDWALK_RECURSE);
3298   }
3299
3300   /* Check a last time for ID not used (fake users only, in theory), and
3301    * do our best for those, using current scene... */
3302   for (i = 0; lb[i]; i++) {
3303     for (id = lb[i]->first; id; id = id->next) {
3304       if (id->tag & LIB_TAG_DOIT) {
3305         previews_id_ensure(C, NULL, id);
3306         id->tag &= ~LIB_TAG_DOIT;
3307       }
3308     }
3309   }
3310
3311   return OPERATOR_FINISHED;
3312 }
3313
3314 static void WM_OT_previews_ensure(wmOperatorType *ot)
3315 {
3316   ot->name = "Refresh Data-Block Previews";
3317   ot->idname = "WM_OT_previews_ensure";
3318   ot->description =
3319       "Ensure data-block previews are available and up-to-date "
3320       "(to be saved in .blend file, only for some types like materials, textures, etc.)";
3321
3322   ot->exec = previews_ensure_exec;
3323 }
3324
3325 /* *************************** Datablocks previews clear ************* */
3326
3327 /* Only types supporting previews currently. */
3328 static const EnumPropertyItem preview_id_type_items[] = {
3329     {FILTER_ID_SCE, "SCENE", 0, "Scenes", ""},
3330     {FILTER_ID_GR, "GROUP", 0, "Groups", ""},
3331     {FILTER_ID_OB, "OBJECT", 0, "Objects", ""},
3332     {FILTER_ID_MA, "MATERIAL", 0, "Materials", ""},
3333     {FILTER_ID_LA, "LIGHT", 0, "Lights", ""},
3334     {FILTER_ID_WO, "WORLD", 0, "Worlds", ""},
3335     {FILTER_ID_TE, "TEXTURE", 0, "Textures", ""},
3336     {FILTER_ID_IM, "IMAGE", 0, "Images", ""},
3337 #if 0 /* XXX TODO */
3338     {FILTER_ID_BR, "BRUSH", 0, "Brushes", ""},
3339 #endif
3340     {0, NULL, 0, NULL, NULL},
3341 };
3342
3343 static int previews_clear_exec(bContext *C, wmOperator *op)
3344 {
3345   Main *bmain = CTX_data_main(C);
3346   ListBase *lb[] = {
3347       &bmain->objects,
3348       &bmain->collections,
3349       &bmain->materials,
3350       &bmain->worlds,
3351       &bmain->lights,
3352       &bmain->textures,
3353       &bmain->images,
3354       NULL,
3355   };
3356   int i;
3357
3358   const int id_filters = RNA_enum_get(op->ptr, "id_type");
3359
3360   for (i = 0; lb[i]; i++) {
3361     ID *id = lb[i]->first;
3362     if (!id) {
3363       continue;
3364     }
3365
3366     //      printf("%s: %d, %d, %d -> %d\n", id->name, GS(id->name), BKE_idcode_to_idfilter(GS(id->name)),
3367     //                                       id_filters, BKE_idcode_to_idfilter(GS(id->name)) & id_filters);
3368
3369     if (!id || !(BKE_idcode_to_idfilter(GS(id->name)) & id_filters)) {
3370       continue;
3371     }
3372
3373     for (; id; id = id->next) {
3374       PreviewImage *prv_img = BKE_previewimg_id_ensure(id);
3375
3376       BKE_previewimg_clear(prv_img);
3377     }
3378   }
3379
3380   return OPERATOR_FINISHED;
3381 }
3382
3383 static void WM_OT_previews_clear(wmOperatorType *ot)
3384 {
3385   ot->name = "Clear Data-Block Previews";
3386   ot->idname = "WM_OT_previews_clear";
3387   ot->description =
3388       "Clear data-block previews (only for some types like objects, materials, textures, etc.)";
3389
3390   ot->exec = previews_clear_exec;
3391   ot->invoke = WM_menu_invoke;
3392
3393   ot->prop = RNA_def_enum_flag(ot->srna,
3394                                "id_type",
3395                                preview_id_type_items,
3396                                FILTER_ID_SCE | FILTER_ID_OB | FILTER_ID_GR | FILTER_ID_MA |
3397                                    FILTER_ID_LA | FILTER_ID_WO | FILTER_ID_TE | FILTER_ID_IM,
3398                                "Data-Block Type",
3399                                "Which data-block previews to clear");
3400 }
3401
3402 /* *************************** Doc from UI ************* */
3403
3404 static int doc_view_manual_ui_context_exec(bContext *C, wmOperator *UNUSED(op))
3405 {
3406   PointerRNA ptr_props;
3407   char buf[512];
3408   short retval = OPERATOR_CANCELLED;
3409
3410   if (UI_but_online_manual_id_from_active(C, buf, sizeof(buf))) {
3411     WM_operator_properties_create(&ptr_props, "WM_OT_doc_view_manual");
3412     RNA_string_set(&ptr_props, "doc_id", buf);
3413
3414     retval = WM_operator_name_call_ptr(
3415         C, WM_operatortype_find("WM_OT_doc_view_manual", false), WM_OP_EXEC_DEFAULT, &ptr_props);
3416
3417     WM_operator_properties_free(&ptr_props);
3418   }
3419
3420   return retval;
3421 }
3422
3423 static void WM_OT_doc_view_manual_ui_context(wmOperatorType *ot)
3424 {
3425   /* identifiers */
3426   ot->name = "View Online Manual";
3427   ot->idname = "WM_OT_doc_view_manual_ui_context";
3428   ot->description = "View a context based online manual in a web browser";
3429
3430   /* callbacks */
3431   ot->poll = ED_operator_regionactive;
3432   ot->exec = doc_view_manual_ui_context_exec;
3433 }
3434
3435 /* ******************************************************* */
3436 /* toggle 3D for current window, turning it fullscreen if needed */
3437 static void WM_OT_stereo3d_set(wmOperatorType *ot)
3438 {
3439   PropertyRNA *prop;
3440
3441   ot->name = "Set Stereo 3D";
3442   ot->idname = "WM_OT_set_stereo_3d";
3443   ot->description = "Toggle 3D stereo support for current window (or change the display mode)";
3444
3445   ot->exec = wm_stereo3d_set_exec;
3446   ot->invoke = wm_stereo3d_set_invoke;
3447   ot->poll = WM_operator_winactive;
3448   ot->ui = wm_stereo3d_set_draw;
3449   ot->check = wm_stereo3d_set_check;
3450   ot->cancel = wm_stereo3d_set_cancel;
3451
3452   prop = RNA_def_enum(ot->srna,
3453                       "display_mode",
3454                       rna_enum_stereo3d_display_items,
3455                       S3D_DISPLAY_ANAGLYPH,
3456                       "Display Mode",
3457                       "");
3458   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3459   prop = RNA_def_enum(ot->srna,
3460                       "anaglyph_type",
3461                       rna_enum_stereo3d_anaglyph_type_items,
3462                       S3D_ANAGLYPH_REDCYAN,
3463                       "Anaglyph Type",
3464                       "");
3465   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3466   prop = RNA_def_enum(ot->srna,
3467                       "interlace_type",
3468                       rna_enum_stereo3d_interlace_type_items,
3469                       S3D_INTERLACE_ROW,
3470                       "Interlace Type",
3471                       "");
3472   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3473   prop = RNA_def_boolean(ot->srna,
3474                          "use_interlace_swap",
3475                          false,
3476                          "Swap Left/Right",
3477                          "Swap left and right stereo channels");
3478   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3479   prop = RNA_def_boolean(ot->srna,
3480                          "use_sidebyside_crosseyed",
3481                          false,
3482                          "Cross-Eyed",
3483                          "Right eye should see left image and vice-versa");
3484   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3485 }
3486
3487 void wm_operatortypes_register(void)
3488 {
3489   WM_operatortype_append(WM_OT_window_close);
3490   WM_operatortype_append(WM_OT_window_new);
3491   WM_operatortype_append(WM_OT_window_new_main);
3492   WM_operatortype_append(WM_OT_read_history);
3493   WM_operatortype_append(WM_OT_read_homefile);
3494   WM_operatortype_append(WM_OT_read_factory_settings);
3495   WM_operatortype_append(WM_OT_save_homefile);
3496   WM_operatortype_append(WM_OT_save_userpref);
3497   WM_operatortype_append(WM_OT_userpref_autoexec_path_add);
3498   WM_operatortype_append(WM_OT_userpref_autoexec_path_remove);
3499   WM_operatortype_append(WM_OT_window_fullscreen_toggle);
3500   WM_operatortype_append(WM_OT_quit_blender);
3501   WM_operatortype_append(WM_OT_open_mainfile);
3502   WM_operatortype_append(WM_OT_revert_mainfile);
3503   WM_operatortype_append(WM_OT_link);
3504   WM_operatortype_append(WM_OT_append);
3505   WM_operatortype_append(WM_OT_lib_relocate);
3506   WM_operatortype_append(WM_OT_lib_reload);
3507   WM_operatortype_append(WM_OT_recover_last_session);
3508   WM_operatortype_append(WM_OT_recover_auto_save);
3509   WM_operatortype_append(WM_OT_save_as_mainfile);
3510   WM_operatortype_append(WM_OT_save_mainfile);
3511   WM_operatortype_append(WM_OT_redraw_timer);
3512   WM_operatortype_append(WM_OT_memory_statistics);
3513   WM_operatortype_append(WM_OT_debug_menu);
3514   WM_operatortype_append(WM_OT_operator_defaults);
3515   WM_operatortype_append(WM_OT_splash);
3516   WM_operatortype_append(WM_OT_search_menu);
3517   WM_operatortype_append(WM_OT_call_menu);
3518   WM_operatortype_append(WM_OT_call_menu_pie);
3519   WM_operatortype_append(WM_OT_call_panel);
3520   WM_operatortype_append(WM_OT_radial_control);
3521   WM_operatortype_append(WM_OT_stereo3d_set);
3522 #if defined(WIN32)
3523   WM_operatortype_append(WM_OT_console_toggle);
3524 #endif
3525   WM_operatortype_append(WM_OT_previews_ensure);
3526   WM_operatortype_append(WM_OT_previews_clear);
3527   WM_operatortype_append(WM_OT_doc_view_manual_ui_context);
3528
3529   /* gizmos */
3530   WM_operatortype_append(GIZMOGROUP_OT_gizmo_select);
3531   WM_operatortype_append(GIZMOGROUP_OT_gizmo_tweak);
3532 }
3533
3534 /* circleselect-like modal operators */
3535 static void gesture_circle_modal_keymap(wmKeyConfig *keyconf)
3536 {
3537   static const EnumPropertyItem modal_items[] = {
3538       {GESTURE_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
3539       {GESTURE_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
3540       {GESTURE_MODAL_CIRCLE_ADD, "ADD", 0, "Add", ""},
3541       {GESTURE_MODAL_CIRCLE_SUB, "SUBTRACT", 0, "Subtract", ""},
3542       {GESTURE_MODAL_CIRCLE_SIZE, "SIZE", 0, "Size", ""},
3543
3544       {GESTURE_MODAL_SELECT, "SELECT", 0, "Select", ""},
3545       {GESTURE_MODAL_DESELECT, "DESELECT", 0, "DeSelect", ""},
3546       {GESTURE_MODAL_NOP, "NOP", 0, "No Operation", ""},
3547
3548       {0, NULL, 0, NULL, NULL},
3549   };
3550
3551   /* WARNING - name is incorrect, use for non-3d views */
3552   wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Gesture Circle");
3553
3554   /* this function is called for each spacetype, only needs to add map once */
3555   if (keymap && keymap->modal_items) {
3556     return;
3557   }
3558
3559   keymap = WM_modalkeymap_add(keyconf, "View3D Gesture Circle", modal_items);
3560
3561   /* assign map to operators */
3562   WM_modalkeymap_assign(keymap, "VIEW3D_OT_select_circle");
3563   WM_modalkeymap_assign(keymap, "UV_OT_select_circle");
3564   WM_modalkeymap_assign(keymap, "CLIP_OT_select_circle");
3565   WM_modalkeymap_assign(keymap, "MASK_OT_select_circle");
3566   WM_modalkeymap_assign(keymap, "NODE_OT_select_circle");
3567   WM_modalkeymap_assign(keymap, "GPENCIL_OT_select_circle");
3568   WM_modalkeymap_assign(keymap, "GRAPH_OT_select_circle");
3569   WM_modalkeymap_assign(keymap, "ACTION_OT_select_circle");
3570 }
3571
3572 /* straight line modal operators */
3573 static void gesture_straightline_modal_keymap(wmKeyConfig *keyconf)
3574 {
3575   static const EnumPropertyItem modal_items[] = {
3576       {GESTURE_MODAL_CANCEL, "CANCEL", 0, "Cancel&