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