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 
17 /** \file
18  * \ingroup wm
19  */
20 
21 #include "BLI_listbase.h"
22 #include "BLI_math.h"
23 
24 #include "BKE_context.h"
25 
26 #include "MEM_guardedalloc.h"
27 
28 #include "RNA_access.h"
29 
30 #include "WM_api.h"
31 #include "WM_message.h"
32 #include "WM_types.h"
33 
34 #include "wm.h"
35 
36 #include "ED_keyframing.h"
37 #include "ED_screen.h"
38 #include "ED_view3d.h"
39 
40 /* own includes */
41 #include "wm_gizmo_intern.h"
42 #include "wm_gizmo_wmapi.h"
43 
44 /* -------------------------------------------------------------------- */
45 /** \name Property Definition
46  * \{ */
47 
wm_gizmo_target_property_array(wmGizmo * gz)48 BLI_INLINE wmGizmoProperty *wm_gizmo_target_property_array(wmGizmo *gz)
49 {
50   return (wmGizmoProperty *)(POINTER_OFFSET(gz, gz->type->struct_size));
51 }
52 
WM_gizmo_target_property_array(wmGizmo * gz)53 wmGizmoProperty *WM_gizmo_target_property_array(wmGizmo *gz)
54 {
55   return wm_gizmo_target_property_array(gz);
56 }
57 
WM_gizmo_target_property_at_index(wmGizmo * gz,int index)58 wmGizmoProperty *WM_gizmo_target_property_at_index(wmGizmo *gz, int index)
59 {
60   BLI_assert(index < gz->type->target_property_defs_len);
61   BLI_assert(index != -1);
62   wmGizmoProperty *gz_prop_array = wm_gizmo_target_property_array(gz);
63   return &gz_prop_array[index];
64 }
65 
WM_gizmo_target_property_find(wmGizmo * gz,const char * idname)66 wmGizmoProperty *WM_gizmo_target_property_find(wmGizmo *gz, const char *idname)
67 {
68   int index = BLI_findstringindex(
69       &gz->type->target_property_defs, idname, offsetof(wmGizmoPropertyType, idname));
70   if (index != -1) {
71     return WM_gizmo_target_property_at_index(gz, index);
72   }
73   return NULL;
74 }
75 
WM_gizmo_target_property_def_rna_ptr(wmGizmo * gz,const wmGizmoPropertyType * gz_prop_type,PointerRNA * ptr,PropertyRNA * prop,int index)76 void WM_gizmo_target_property_def_rna_ptr(wmGizmo *gz,
77                                           const wmGizmoPropertyType *gz_prop_type,
78                                           PointerRNA *ptr,
79                                           PropertyRNA *prop,
80                                           int index)
81 {
82   wmGizmoProperty *gz_prop = WM_gizmo_target_property_at_index(gz, gz_prop_type->index_in_type);
83 
84   /* if gizmo evokes an operator we cannot use it for property manipulation */
85   BLI_assert(gz->op_data == NULL);
86   BLI_assert(prop != NULL);
87 
88   gz_prop->type = gz_prop_type;
89 
90   gz_prop->ptr = *ptr;
91   gz_prop->prop = prop;
92   gz_prop->index = index;
93 
94   if (gz->type->property_update) {
95     gz->type->property_update(gz, gz_prop);
96   }
97 }
98 
WM_gizmo_target_property_def_rna(wmGizmo * gz,const char * idname,PointerRNA * ptr,const char * propname,int index)99 void WM_gizmo_target_property_def_rna(
100     wmGizmo *gz, const char *idname, PointerRNA *ptr, const char *propname, int index)
101 {
102   const wmGizmoPropertyType *gz_prop_type = WM_gizmotype_target_property_find(gz->type, idname);
103   PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
104   if (prop == NULL) {
105     RNA_warning("%s: %s.%s not found", __func__, RNA_struct_identifier(ptr->type), propname);
106   }
107   WM_gizmo_target_property_def_rna_ptr(gz, gz_prop_type, ptr, prop, index);
108 }
109 
WM_gizmo_target_property_def_func_ptr(wmGizmo * gz,const wmGizmoPropertyType * gz_prop_type,const wmGizmoPropertyFnParams * params)110 void WM_gizmo_target_property_def_func_ptr(wmGizmo *gz,
111                                            const wmGizmoPropertyType *gz_prop_type,
112                                            const wmGizmoPropertyFnParams *params)
113 {
114   wmGizmoProperty *gz_prop = WM_gizmo_target_property_at_index(gz, gz_prop_type->index_in_type);
115 
116   /* if gizmo evokes an operator we cannot use it for property manipulation */
117   BLI_assert(gz->op_data == NULL);
118 
119   gz_prop->type = gz_prop_type;
120 
121   gz_prop->custom_func.value_get_fn = params->value_get_fn;
122   gz_prop->custom_func.value_set_fn = params->value_set_fn;
123   gz_prop->custom_func.range_get_fn = params->range_get_fn;
124   gz_prop->custom_func.free_fn = params->free_fn;
125   gz_prop->custom_func.user_data = params->user_data;
126 
127   if (gz->type->property_update) {
128     gz->type->property_update(gz, gz_prop);
129   }
130 }
131 
WM_gizmo_target_property_def_func(wmGizmo * gz,const char * idname,const wmGizmoPropertyFnParams * params)132 void WM_gizmo_target_property_def_func(wmGizmo *gz,
133                                        const char *idname,
134                                        const wmGizmoPropertyFnParams *params)
135 {
136   const wmGizmoPropertyType *gz_prop_type = WM_gizmotype_target_property_find(gz->type, idname);
137   WM_gizmo_target_property_def_func_ptr(gz, gz_prop_type, params);
138 }
139 
WM_gizmo_target_property_clear_rna_ptr(wmGizmo * gz,const wmGizmoPropertyType * gz_prop_type)140 void WM_gizmo_target_property_clear_rna_ptr(wmGizmo *gz, const wmGizmoPropertyType *gz_prop_type)
141 {
142   wmGizmoProperty *gz_prop = WM_gizmo_target_property_at_index(gz, gz_prop_type->index_in_type);
143 
144   /* if gizmo evokes an operator we cannot use it for property manipulation */
145   BLI_assert(gz->op_data == NULL);
146 
147   gz_prop->type = NULL;
148 
149   gz_prop->ptr = PointerRNA_NULL;
150   gz_prop->prop = NULL;
151   gz_prop->index = -1;
152 }
153 
WM_gizmo_target_property_clear_rna(wmGizmo * gz,const char * idname)154 void WM_gizmo_target_property_clear_rna(wmGizmo *gz, const char *idname)
155 {
156   const wmGizmoPropertyType *gz_prop_type = WM_gizmotype_target_property_find(gz->type, idname);
157   WM_gizmo_target_property_clear_rna_ptr(gz, gz_prop_type);
158 }
159 
160 /** \} */
161 
162 /* -------------------------------------------------------------------- */
163 /** \name Property Access
164  * \{ */
165 
WM_gizmo_target_property_is_valid_any(wmGizmo * gz)166 bool WM_gizmo_target_property_is_valid_any(wmGizmo *gz)
167 {
168   wmGizmoProperty *gz_prop_array = wm_gizmo_target_property_array(gz);
169   for (int i = 0; i < gz->type->target_property_defs_len; i++) {
170     wmGizmoProperty *gz_prop = &gz_prop_array[i];
171     if (WM_gizmo_target_property_is_valid(gz_prop)) {
172       return true;
173     }
174   }
175   return false;
176 }
177 
WM_gizmo_target_property_is_valid(const wmGizmoProperty * gz_prop)178 bool WM_gizmo_target_property_is_valid(const wmGizmoProperty *gz_prop)
179 {
180   return ((gz_prop->prop != NULL) ||
181           (gz_prop->custom_func.value_get_fn && gz_prop->custom_func.value_set_fn));
182 }
183 
WM_gizmo_target_property_float_get(const wmGizmo * gz,wmGizmoProperty * gz_prop)184 float WM_gizmo_target_property_float_get(const wmGizmo *gz, wmGizmoProperty *gz_prop)
185 {
186   if (gz_prop->custom_func.value_get_fn) {
187     float value = 0.0f;
188     BLI_assert(gz_prop->type->array_length == 1);
189     gz_prop->custom_func.value_get_fn(gz, gz_prop, &value);
190     return value;
191   }
192 
193   if (gz_prop->index == -1) {
194     return RNA_property_float_get(&gz_prop->ptr, gz_prop->prop);
195   }
196   return RNA_property_float_get_index(&gz_prop->ptr, gz_prop->prop, gz_prop->index);
197 }
198 
WM_gizmo_target_property_float_set(bContext * C,const wmGizmo * gz,wmGizmoProperty * gz_prop,const float value)199 void WM_gizmo_target_property_float_set(bContext *C,
200                                         const wmGizmo *gz,
201                                         wmGizmoProperty *gz_prop,
202                                         const float value)
203 {
204   if (gz_prop->custom_func.value_set_fn) {
205     BLI_assert(gz_prop->type->array_length == 1);
206     gz_prop->custom_func.value_set_fn(gz, gz_prop, &value);
207     return;
208   }
209 
210   /* reset property */
211   if (gz_prop->index == -1) {
212     RNA_property_float_set(&gz_prop->ptr, gz_prop->prop, value);
213   }
214   else {
215     RNA_property_float_set_index(&gz_prop->ptr, gz_prop->prop, gz_prop->index, value);
216   }
217   RNA_property_update(C, &gz_prop->ptr, gz_prop->prop);
218 }
219 
WM_gizmo_target_property_float_get_array(const wmGizmo * gz,wmGizmoProperty * gz_prop,float * value)220 void WM_gizmo_target_property_float_get_array(const wmGizmo *gz,
221                                               wmGizmoProperty *gz_prop,
222                                               float *value)
223 {
224   if (gz_prop->custom_func.value_get_fn) {
225     gz_prop->custom_func.value_get_fn(gz, gz_prop, value);
226     return;
227   }
228   RNA_property_float_get_array(&gz_prop->ptr, gz_prop->prop, value);
229 }
230 
WM_gizmo_target_property_float_set_array(bContext * C,const wmGizmo * gz,wmGizmoProperty * gz_prop,const float * value)231 void WM_gizmo_target_property_float_set_array(bContext *C,
232                                               const wmGizmo *gz,
233                                               wmGizmoProperty *gz_prop,
234                                               const float *value)
235 {
236   if (gz_prop->custom_func.value_set_fn) {
237     gz_prop->custom_func.value_set_fn(gz, gz_prop, value);
238     return;
239   }
240   RNA_property_float_set_array(&gz_prop->ptr, gz_prop->prop, value);
241 
242   RNA_property_update(C, &gz_prop->ptr, gz_prop->prop);
243 }
244 
WM_gizmo_target_property_float_range_get(const wmGizmo * gz,wmGizmoProperty * gz_prop,float range[2])245 bool WM_gizmo_target_property_float_range_get(const wmGizmo *gz,
246                                               wmGizmoProperty *gz_prop,
247                                               float range[2])
248 {
249   if (gz_prop->custom_func.value_get_fn) {
250     if (gz_prop->custom_func.range_get_fn) {
251       gz_prop->custom_func.range_get_fn(gz, gz_prop, range);
252       return true;
253     }
254     return false;
255   }
256 
257   float step, precision;
258   RNA_property_float_ui_range(
259       &gz_prop->ptr, gz_prop->prop, &range[0], &range[1], &step, &precision);
260   return true;
261 }
262 
WM_gizmo_target_property_array_length(const wmGizmo * UNUSED (gz),wmGizmoProperty * gz_prop)263 int WM_gizmo_target_property_array_length(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop)
264 {
265   if (gz_prop->custom_func.value_get_fn) {
266     return gz_prop->type->array_length;
267   }
268   return RNA_property_array_length(&gz_prop->ptr, gz_prop->prop);
269 }
270 
271 /** \} */
272 
273 /* -------------------------------------------------------------------- */
274 /** \name Property Define
275  * \{ */
276 
WM_gizmotype_target_property_find(const wmGizmoType * gzt,const char * idname)277 const wmGizmoPropertyType *WM_gizmotype_target_property_find(const wmGizmoType *gzt,
278                                                              const char *idname)
279 {
280   return BLI_findstring(&gzt->target_property_defs, idname, offsetof(wmGizmoPropertyType, idname));
281 }
282 
WM_gizmotype_target_property_def(wmGizmoType * gzt,const char * idname,int data_type,int array_length)283 void WM_gizmotype_target_property_def(wmGizmoType *gzt,
284                                       const char *idname,
285                                       int data_type,
286                                       int array_length)
287 {
288   wmGizmoPropertyType *mpt;
289 
290   BLI_assert(WM_gizmotype_target_property_find(gzt, idname) == NULL);
291 
292   const uint idname_size = strlen(idname) + 1;
293   mpt = MEM_callocN(sizeof(wmGizmoPropertyType) + idname_size, __func__);
294   memcpy(mpt->idname, idname, idname_size);
295   mpt->data_type = data_type;
296   mpt->array_length = array_length;
297   mpt->index_in_type = gzt->target_property_defs_len;
298   gzt->target_property_defs_len += 1;
299   BLI_addtail(&gzt->target_property_defs, mpt);
300 }
301 
302 /** \} */
303 
304 /* -------------------------------------------------------------------- */
305 /** \name Property Utilities
306  * \{ */
307 
WM_gizmo_do_msg_notify_tag_refresh(bContext * UNUSED (C),wmMsgSubscribeKey * UNUSED (msg_key),wmMsgSubscribeValue * msg_val)308 void WM_gizmo_do_msg_notify_tag_refresh(bContext *UNUSED(C),
309                                         wmMsgSubscribeKey *UNUSED(msg_key),
310                                         wmMsgSubscribeValue *msg_val)
311 {
312   ARegion *region = msg_val->owner;
313   wmGizmoMap *gzmap = msg_val->user_data;
314 
315   ED_region_tag_redraw(
316       region); /* Could possibly avoid a full redraw and only tag for editor overlays
317                 * redraw in some cases, see #ED_region_tag_redraw_editor_overlays(). */
318   WM_gizmomap_tag_refresh(gzmap);
319 }
320 
321 /**
322  * Runs on the "prepare draw" pass,
323  * drawing the region clears.
324  */
WM_gizmo_target_property_subscribe_all(wmGizmo * gz,struct wmMsgBus * mbus,ARegion * region)325 void WM_gizmo_target_property_subscribe_all(wmGizmo *gz, struct wmMsgBus *mbus, ARegion *region)
326 {
327   if (gz->type->target_property_defs_len) {
328     wmGizmoProperty *gz_prop_array = WM_gizmo_target_property_array(gz);
329     for (int i = 0; i < gz->type->target_property_defs_len; i++) {
330       wmGizmoProperty *gz_prop = &gz_prop_array[i];
331       if (WM_gizmo_target_property_is_valid(gz_prop)) {
332         if (gz_prop->prop) {
333           WM_msg_subscribe_rna(mbus,
334                                &gz_prop->ptr,
335                                gz_prop->prop,
336                                &(const wmMsgSubscribeValue){
337                                    .owner = region,
338                                    .user_data = region,
339                                    .notify = ED_region_do_msg_notify_tag_redraw,
340                                },
341                                __func__);
342           WM_msg_subscribe_rna(mbus,
343                                &gz_prop->ptr,
344                                gz_prop->prop,
345                                &(const wmMsgSubscribeValue){
346                                    .owner = region,
347                                    .user_data = gz->parent_gzgroup->parent_gzmap,
348                                    .notify = WM_gizmo_do_msg_notify_tag_refresh,
349                                },
350                                __func__);
351         }
352       }
353     }
354   }
355 }
356 
357 /**
358  * Auto-key function if auto-key is enabled.
359  */
WM_gizmo_target_property_anim_autokey(bContext * C,const wmGizmo * UNUSED (gz),wmGizmoProperty * gz_prop)360 void WM_gizmo_target_property_anim_autokey(bContext *C,
361                                            const wmGizmo *UNUSED(gz),
362                                            wmGizmoProperty *gz_prop)
363 {
364   if (gz_prop->prop != NULL) {
365     Scene *scene = CTX_data_scene(C);
366     const float cfra = (float)CFRA;
367     const int index = gz_prop->index == -1 ? 0 : gz_prop->index;
368     ED_autokeyframe_property(C, scene, &gz_prop->ptr, gz_prop->prop, index, cfra);
369   }
370 }
371 
372 /** \} */
373