Cleanup: blank lines over doxy headers
[blender.git] / source / blender / windowmanager / gizmo / intern / wm_gizmo.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2014 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/windowmanager/gizmo/intern/wm_gizmo.c
27  *  \ingroup wm
28  */
29
30 #include "MEM_guardedalloc.h"
31
32 #include "BLI_listbase.h"
33 #include "BLI_math.h"
34
35 #include "BKE_context.h"
36
37 #include "GPU_batch.h"
38 #include "GPU_glew.h"
39 #include "GPU_immediate.h"
40
41 #include "RNA_access.h"
42 #include "RNA_define.h"
43
44 #include "BKE_global.h"
45 #include "BKE_main.h"
46 #include "BKE_idprop.h"
47
48 #include "WM_api.h"
49 #include "WM_toolsystem.h"
50 #include "WM_types.h"
51
52 #include "ED_screen.h"
53 #include "ED_view3d.h"
54
55 #include "UI_interface.h"
56
57 #ifdef WITH_PYTHON
58 #include "BPY_extern.h"
59 #endif
60
61 /* only for own init/exit calls (wm_gizmotype_init/wm_gizmotype_free) */
62 #include "wm.h"
63
64 /* own includes */
65 #include "wm_gizmo_wmapi.h"
66 #include "wm_gizmo_intern.h"
67
68 static void wm_gizmo_register(
69         wmGizmoGroup *gzgroup, wmGizmo *gz);
70
71 /**
72  * \note Follow #wm_operator_create convention.
73  */
74 static wmGizmo *wm_gizmo_create(
75         const wmGizmoType *gzt,
76         PointerRNA *properties)
77 {
78         BLI_assert(gzt != NULL);
79         BLI_assert(gzt->struct_size >= sizeof(wmGizmo));
80
81         wmGizmo *gz = MEM_callocN(
82                 gzt->struct_size + (sizeof(wmGizmoProperty) * gzt->target_property_defs_len), __func__);
83         gz->type = gzt;
84
85         /* initialize properties, either copy or create */
86         gz->ptr = MEM_callocN(sizeof(PointerRNA), "wmGizmoPtrRNA");
87         if (properties && properties->data) {
88                 gz->properties = IDP_CopyProperty(properties->data);
89         }
90         else {
91                 IDPropertyTemplate val = {0};
92                 gz->properties = IDP_New(IDP_GROUP, &val, "wmGizmoProperties");
93         }
94         RNA_pointer_create(G_MAIN->wm.first, gzt->srna, gz->properties, gz->ptr);
95
96         WM_gizmo_properties_sanitize(gz->ptr, 0);
97
98         unit_m4(gz->matrix_space);
99         unit_m4(gz->matrix_basis);
100         unit_m4(gz->matrix_offset);
101
102         gz->drag_part = -1;
103
104         return gz;
105 }
106
107 wmGizmo *WM_gizmo_new_ptr(
108         const wmGizmoType *gzt, wmGizmoGroup *gzgroup,
109         PointerRNA *properties)
110 {
111         wmGizmo *gz = wm_gizmo_create(gzt, properties);
112
113         wm_gizmo_register(gzgroup, gz);
114
115         if (gz->type->setup != NULL) {
116                 gz->type->setup(gz);
117         }
118
119         return gz;
120 }
121
122 /**
123  * \param name: Must be a valid gizmo type name,
124  * if you need to check it exists use #WM_gizmo_new_ptr
125  * because callers of this function don't NULL check the return value.
126  */
127 wmGizmo *WM_gizmo_new(
128         const char *idname, wmGizmoGroup *gzgroup,
129         PointerRNA *properties)
130 {
131         const wmGizmoType *gzt = WM_gizmotype_find(idname, false);
132         return WM_gizmo_new_ptr(gzt, gzgroup, properties);
133 }
134
135 /**
136  * Initialize default values and allocate needed memory for members.
137  */
138 static void gizmo_init(wmGizmo *gz)
139 {
140         const float color_default[4] = {1.0f, 1.0f, 1.0f, 1.0f};
141
142         gz->scale_basis = 1.0f;
143         gz->line_width = 1.0f;
144
145         /* defaults */
146         copy_v4_v4(gz->color, color_default);
147         copy_v4_v4(gz->color_hi, color_default);
148 }
149
150 /**
151  * Register \a gizmo.
152  *
153  * \note Not to be confused with type registration from RNA.
154  */
155 static void wm_gizmo_register(wmGizmoGroup *gzgroup, wmGizmo *gz)
156 {
157         gizmo_init(gz);
158         wm_gizmogroup_gizmo_register(gzgroup, gz);
159 }
160
161 /**
162  * \warning this doesn't check #wmGizmoMap (highlight, selection etc).
163  * Typical use is when freeing the windowing data,
164  * where caller can manage clearing selection, highlight... etc.
165  */
166 void WM_gizmo_free(wmGizmo *gz)
167 {
168         if (gz->type->free != NULL) {
169                 gz->type->free(gz);
170         }
171
172 #ifdef WITH_PYTHON
173         if (gz->py_instance) {
174                 /* do this first in case there are any __del__ functions or
175                  * similar that use properties */
176                 BPY_DECREF_RNA_INVALIDATE(gz->py_instance);
177         }
178 #endif
179
180         if (gz->op_data) {
181                 for (int i = 0; i < gz->op_data_len; i++) {
182                         WM_operator_properties_free(&gz->op_data[i].ptr);
183                 }
184                 MEM_freeN(gz->op_data);
185         }
186
187         if (gz->ptr != NULL) {
188                 WM_gizmo_properties_free(gz->ptr);
189                 MEM_freeN(gz->ptr);
190         }
191
192         if (gz->type->target_property_defs_len != 0) {
193                 wmGizmoProperty *gz_prop_array = WM_gizmo_target_property_array(gz);
194                 for (int i = 0; i < gz->type->target_property_defs_len; i++) {
195                         wmGizmoProperty *gz_prop = &gz_prop_array[i];
196                         if (gz_prop->custom_func.free_fn) {
197                                 gz_prop->custom_func.free_fn(gz, gz_prop);
198                         }
199                 }
200         }
201
202         MEM_freeN(gz);
203 }
204
205 /**
206  * Free \a gizmo and unlink from \a gizmolist.
207  * \a gizmolist is allowed to be NULL.
208  */
209 void WM_gizmo_unlink(ListBase *gizmolist, wmGizmoMap *gzmap, wmGizmo *gz, bContext *C)
210 {
211         if (gz->state & WM_GIZMO_STATE_HIGHLIGHT) {
212                 wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
213         }
214         if (gz->state & WM_GIZMO_STATE_MODAL) {
215                 wm_gizmomap_modal_set(gzmap, C, gz, NULL, false);
216         }
217         /* Unlink instead of setting so we don't run callbacks. */
218         if (gz->state & WM_GIZMO_STATE_SELECT) {
219                 WM_gizmo_select_unlink(gzmap, gz);
220         }
221
222         if (gizmolist) {
223                 BLI_remlink(gizmolist, gz);
224         }
225
226         BLI_assert(gzmap->gzmap_context.highlight != gz);
227         BLI_assert(gzmap->gzmap_context.modal != gz);
228
229         WM_gizmo_free(gz);
230 }
231
232 /* -------------------------------------------------------------------- */
233 /** \name Gizmo Creation API
234  *
235  * API for defining data on gizmo creation.
236  *
237  * \{ */
238
239 struct wmGizmoOpElem *WM_gizmo_operator_get(
240         wmGizmo *gz, int part_index)
241 {
242         if (gz->op_data && ((part_index >= 0) && (part_index < gz->op_data_len))) {
243                 return &gz->op_data[part_index];
244         }
245         return NULL;
246 }
247
248 PointerRNA *WM_gizmo_operator_set(
249         wmGizmo *gz, int part_index,
250         wmOperatorType *ot, IDProperty *properties)
251 {
252         BLI_assert(part_index < 255);
253         /* We could pre-allocate these but using multiple is such a rare thing. */
254         if (part_index >= gz->op_data_len) {
255                 gz->op_data_len = part_index + 1;
256                 gz->op_data = MEM_recallocN(gz->op_data, sizeof(*gz->op_data) * gz->op_data_len);
257         }
258         wmGizmoOpElem *gzop = &gz->op_data[part_index];
259         gzop->type = ot;
260
261         if (gzop->ptr.data) {
262                 WM_operator_properties_free(&gzop->ptr);
263         }
264         WM_operator_properties_create_ptr(&gzop->ptr, ot);
265
266         if (properties) {
267                 gzop->ptr.data = properties;
268         }
269
270         return &gzop->ptr;
271 }
272
273 int WM_gizmo_operator_invoke(bContext *C, wmGizmo *gz, wmGizmoOpElem *gzop)
274 {
275         if (gz->flag & WM_GIZMO_OPERATOR_TOOL_INIT) {
276                 /* Merge toolsettings into the gizmo properties. */
277                 PointerRNA tref_ptr;
278                 bToolRef *tref = WM_toolsystem_ref_from_context(C);
279                 if (tref && WM_toolsystem_ref_properties_get_from_operator(tref, gzop->type, &tref_ptr)) {
280                         if (gzop->ptr.data == NULL) {
281                                 IDPropertyTemplate val = {0};
282                                 gzop->ptr.data = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
283                         }
284                         IDP_MergeGroup(gzop->ptr.data, tref_ptr.data, false);
285                 }
286         }
287         return WM_operator_name_call_ptr(C, gzop->type, WM_OP_INVOKE_DEFAULT, &gzop->ptr);
288 }
289
290 static void wm_gizmo_set_matrix_rotation_from_z_axis__internal(
291         float matrix[4][4], const float z_axis[3])
292 {
293         /* old code, seems we can use simpler method */
294 #if 0
295         const float z_global[3] = {0.0f, 0.0f, 1.0f};
296         float rot[3][3];
297
298         rotation_between_vecs_to_mat3(rot, z_global, z_axis);
299         copy_v3_v3(matrix[0], rot[0]);
300         copy_v3_v3(matrix[1], rot[1]);
301         copy_v3_v3(matrix[2], rot[2]);
302 #else
303         normalize_v3_v3(matrix[2], z_axis);
304         ortho_basis_v3v3_v3(matrix[0], matrix[1], matrix[2]);
305 #endif
306
307 }
308
309 static void wm_gizmo_set_matrix_rotation_from_yz_axis__internal(
310         float matrix[4][4], const float y_axis[3], const float z_axis[3])
311 {
312         normalize_v3_v3(matrix[1], y_axis);
313         normalize_v3_v3(matrix[2], z_axis);
314         cross_v3_v3v3(matrix[0], matrix[1], matrix[2]);
315         normalize_v3(matrix[0]);
316 }
317
318 /**
319  * wmGizmo.matrix utils.
320  */
321 void WM_gizmo_set_matrix_rotation_from_z_axis(
322         wmGizmo *gz, const float z_axis[3])
323 {
324         wm_gizmo_set_matrix_rotation_from_z_axis__internal(gz->matrix_basis, z_axis);
325 }
326 void WM_gizmo_set_matrix_rotation_from_yz_axis(
327         wmGizmo *gz, const float y_axis[3], const float z_axis[3])
328 {
329         wm_gizmo_set_matrix_rotation_from_yz_axis__internal(gz->matrix_basis, y_axis, z_axis);
330 }
331 void WM_gizmo_set_matrix_location(wmGizmo *gz, const float origin[3])
332 {
333         copy_v3_v3(gz->matrix_basis[3], origin);
334 }
335
336 /**
337  * wmGizmo.matrix_offset utils.
338  */
339 void WM_gizmo_set_matrix_offset_rotation_from_z_axis(
340         wmGizmo *gz, const float z_axis[3])
341 {
342         wm_gizmo_set_matrix_rotation_from_z_axis__internal(gz->matrix_offset, z_axis);
343 }
344 void WM_gizmo_set_matrix_offset_rotation_from_yz_axis(
345         wmGizmo *gz, const float y_axis[3], const float z_axis[3])
346 {
347         wm_gizmo_set_matrix_rotation_from_yz_axis__internal(gz->matrix_offset, y_axis, z_axis);
348 }
349 void WM_gizmo_set_matrix_offset_location(wmGizmo *gz, const float offset[3])
350 {
351         copy_v3_v3(gz->matrix_offset[3], offset);
352 }
353
354 void WM_gizmo_set_flag(wmGizmo *gz, const int flag, const bool enable)
355 {
356         if (enable) {
357                 gz->flag |= flag;
358         }
359         else {
360                 gz->flag &= ~flag;
361         }
362 }
363
364 void WM_gizmo_set_scale(wmGizmo *gz, const float scale)
365 {
366         gz->scale_basis = scale;
367 }
368
369 void WM_gizmo_set_line_width(wmGizmo *gz, const float line_width)
370 {
371         gz->line_width = line_width;
372 }
373
374 void WM_gizmo_get_color(const wmGizmo *gz, float color[4])
375 {
376         copy_v4_v4(color, gz->color);
377 }
378 void WM_gizmo_set_color(wmGizmo *gz, const float color[4])
379 {
380         copy_v4_v4(gz->color, color);
381 }
382
383 void WM_gizmo_get_color_highlight(const wmGizmo *gz, float color_hi[4])
384 {
385         copy_v4_v4(color_hi, gz->color_hi);
386 }
387 void WM_gizmo_set_color_highlight(wmGizmo *gz, const float color_hi[4])
388 {
389         copy_v4_v4(gz->color_hi, color_hi);
390 }
391
392
393 /** \} */ // Gizmo Creation API
394
395
396 /* -------------------------------------------------------------------- */
397 /** \name Gizmo Callback Assignment
398  *
399  * \{ */
400
401 void WM_gizmo_set_fn_custom_modal(struct wmGizmo *gz, wmGizmoFnModal fn)
402 {
403         gz->custom_modal = fn;
404 }
405
406 /** \} */
407
408
409 /* -------------------------------------------------------------------- */
410 /**
411  * Add/Remove \a gizmo to selection.
412  * Reallocates memory for selected gizmos so better not call for selecting multiple ones.
413  *
414  * \return if the selection has changed.
415  */
416 bool wm_gizmo_select_set_ex(
417         wmGizmoMap *gzmap, wmGizmo *gz, bool select,
418         bool use_array, bool use_callback)
419 {
420         bool changed = false;
421
422         if (select) {
423                 if ((gz->state & WM_GIZMO_STATE_SELECT) == 0) {
424                         if (use_array) {
425                                 wm_gizmomap_select_array_push_back(gzmap, gz);
426                         }
427                         gz->state |= WM_GIZMO_STATE_SELECT;
428                         changed = true;
429                 }
430         }
431         else {
432                 if (gz->state & WM_GIZMO_STATE_SELECT) {
433                         if (use_array) {
434                                 wm_gizmomap_select_array_remove(gzmap, gz);
435                         }
436                         gz->state &= ~WM_GIZMO_STATE_SELECT;
437                         changed = true;
438                 }
439         }
440
441         /* In the case of unlinking we only want to remove from the array
442          * and not write to the external state */
443         if (use_callback && changed) {
444                 if (gz->type->select_refresh) {
445                         gz->type->select_refresh(gz);
446                 }
447         }
448
449         return changed;
450 }
451
452 /* Remove from selection array without running callbacks. */
453 bool WM_gizmo_select_unlink(wmGizmoMap *gzmap, wmGizmo *gz)
454 {
455         return wm_gizmo_select_set_ex(gzmap, gz, false, true, false);
456 }
457
458 bool WM_gizmo_select_set(wmGizmoMap *gzmap, wmGizmo *gz, bool select)
459 {
460         return wm_gizmo_select_set_ex(gzmap, gz, select, true, true);
461 }
462
463 void WM_gizmo_highlight_set(wmGizmoMap *gzmap, wmGizmo *gz)
464 {
465         wm_gizmomap_highlight_set(gzmap, NULL, gz, gz ? gz->highlight_part : 0);
466 }
467
468 bool wm_gizmo_select_and_highlight(bContext *C, wmGizmoMap *gzmap, wmGizmo *gz)
469 {
470         if (WM_gizmo_select_set(gzmap, gz, true)) {
471                 wm_gizmomap_highlight_set(gzmap, C, gz, gz->highlight_part);
472                 return true;
473         }
474         else {
475                 return false;
476         }
477 }
478
479 /**
480  * Special function to run from setup so gizmos start out interactive.
481  *
482  * We could do this when linking them, but this complicates things since the window update code needs to run first.
483  */
484 void WM_gizmo_modal_set_from_setup(
485         struct wmGizmoMap *gzmap, struct bContext *C,
486         struct wmGizmo *gz, int part_index, const wmEvent *event)
487 {
488         gz->highlight_part = part_index;
489         WM_gizmo_highlight_set(gzmap, gz);
490         if (false) {
491                 wm_gizmomap_modal_set(gzmap, C, gz, event, true);
492         }
493         else {
494                 /* WEAK: but it works. */
495                 WM_operator_name_call(C, "GIZMOGROUP_OT_gizmo_tweak", WM_OP_INVOKE_DEFAULT, NULL);
496         }
497 }
498
499 void wm_gizmo_calculate_scale(wmGizmo *gz, const bContext *C)
500 {
501         const RegionView3D *rv3d = CTX_wm_region_view3d(C);
502         float scale = UI_DPI_FAC;
503
504         if ((gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_SCALE) == 0) {
505                 scale *= U.gizmo_size;
506                 if (rv3d) {
507                         /* 'ED_view3d_pixel_size' includes 'U.pixelsize', remove it. */
508                         float matrix_world[4][4];
509                         if (gz->type->matrix_basis_get) {
510                                 float matrix_basis[4][4];
511                                 gz->type->matrix_basis_get(gz, matrix_basis);
512                                 mul_m4_m4m4(matrix_world, gz->matrix_space, matrix_basis);
513                         }
514                         else {
515                                 mul_m4_m4m4(matrix_world, gz->matrix_space, gz->matrix_basis);
516                         }
517
518                         /* Exclude matrix_offset from scale. */
519                         scale *= ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_world[3]);
520                 }
521                 else {
522                         scale *= 0.02f;
523                 }
524         }
525
526         gz->scale_final = gz->scale_basis * scale;
527 }
528
529 static void gizmo_update_prop_data(wmGizmo *gz)
530 {
531         /* gizmo property might have been changed, so update gizmo */
532         if (gz->type->property_update) {
533                 wmGizmoProperty *gz_prop_array = WM_gizmo_target_property_array(gz);
534                 for (int i = 0; i < gz->type->target_property_defs_len; i++) {
535                         wmGizmoProperty *gz_prop = &gz_prop_array[i];
536                         if (WM_gizmo_target_property_is_valid(gz_prop)) {
537                                 gz->type->property_update(gz, gz_prop);
538                         }
539                 }
540         }
541 }
542
543 void wm_gizmo_update(wmGizmo *gz, const bContext *C, const bool refresh_map)
544 {
545         if (refresh_map) {
546                 gizmo_update_prop_data(gz);
547         }
548         wm_gizmo_calculate_scale(gz, C);
549 }
550
551 int wm_gizmo_is_visible(wmGizmo *gz)
552 {
553         if (gz->flag & WM_GIZMO_HIDDEN) {
554                 return 0;
555         }
556         if ((gz->state & WM_GIZMO_STATE_MODAL) &&
557             !(gz->flag & (WM_GIZMO_DRAW_MODAL | WM_GIZMO_DRAW_VALUE)))
558         {
559                 /* don't draw while modal (dragging) */
560                 return 0;
561         }
562         if ((gz->flag & WM_GIZMO_DRAW_HOVER) &&
563             !(gz->state & WM_GIZMO_STATE_HIGHLIGHT) &&
564             !(gz->state & WM_GIZMO_STATE_SELECT)) /* still draw selected gizmos */
565         {
566                 /* update but don't draw */
567                 return WM_GIZMO_IS_VISIBLE_UPDATE;
568         }
569
570         return WM_GIZMO_IS_VISIBLE_UPDATE | WM_GIZMO_IS_VISIBLE_DRAW;
571 }
572
573 void WM_gizmo_calc_matrix_final_params(
574         const wmGizmo *gz,
575         const struct WM_GizmoMatrixParams *params,
576         float r_mat[4][4])
577 {
578         const float (* const matrix_space)[4]  = params->matrix_space  ? params->matrix_space  : gz->matrix_space;
579         const float (* const matrix_basis)[4]  = params->matrix_basis  ? params->matrix_basis  : gz->matrix_basis;
580         const float (* const matrix_offset)[4] = params->matrix_offset ? params->matrix_offset : gz->matrix_offset;
581         const float *scale_final = params->scale_final ? params->scale_final : &gz->scale_final;
582
583         float final_matrix[4][4];
584         if (params->matrix_basis == NULL && gz->type->matrix_basis_get) {
585                 gz->type->matrix_basis_get(gz, final_matrix);
586         }
587         else {
588                 copy_m4_m4(final_matrix, matrix_basis);
589         }
590
591         if (gz->flag & WM_GIZMO_DRAW_NO_SCALE) {
592                 mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
593         }
594         else {
595                 if (gz->flag & WM_GIZMO_DRAW_OFFSET_SCALE) {
596                         mul_mat3_m4_fl(final_matrix, *scale_final);
597                         mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
598                 }
599                 else {
600                         mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
601                         mul_mat3_m4_fl(final_matrix, *scale_final);
602                 }
603         }
604
605         mul_m4_m4m4(r_mat, matrix_space, final_matrix);
606 }
607
608 void WM_gizmo_calc_matrix_final_no_offset(const wmGizmo *gz, float r_mat[4][4])
609 {
610         float mat_identity[4][4];
611         unit_m4(mat_identity);
612
613         WM_gizmo_calc_matrix_final_params(
614                 gz,
615                 &((struct WM_GizmoMatrixParams) {
616                     .matrix_space = NULL,
617                     .matrix_basis = NULL,
618                     .matrix_offset = mat_identity,
619                     .scale_final = NULL,
620                 }), r_mat
621         );
622 }
623
624 void WM_gizmo_calc_matrix_final(const wmGizmo *gz, float r_mat[4][4])
625 {
626         WM_gizmo_calc_matrix_final_params(
627                 gz,
628                 &((struct WM_GizmoMatrixParams) {
629                     .matrix_space = NULL,
630                     .matrix_basis = NULL,
631                     .matrix_offset = NULL,
632                     .scale_final = NULL,
633                 }), r_mat
634         );
635 }
636
637 /** \name Gizmo Property Access
638  *
639  * Matches `WM_operator_properties` conventions.
640  *
641  * \{ */
642
643
644 void WM_gizmo_properties_create_ptr(PointerRNA *ptr, wmGizmoType *gzt)
645 {
646         RNA_pointer_create(NULL, gzt->srna, NULL, ptr);
647 }
648
649 void WM_gizmo_properties_create(PointerRNA *ptr, const char *gtstring)
650 {
651         const wmGizmoType *gzt = WM_gizmotype_find(gtstring, false);
652
653         if (gzt)
654                 WM_gizmo_properties_create_ptr(ptr, (wmGizmoType *)gzt);
655         else
656                 RNA_pointer_create(NULL, &RNA_GizmoProperties, NULL, ptr);
657 }
658
659 /* similar to the function above except its uses ID properties
660  * used for keymaps and macros */
661 void WM_gizmo_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *gtstring)
662 {
663         if (*properties == NULL) {
664                 IDPropertyTemplate val = {0};
665                 *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
666         }
667
668         if (*ptr == NULL) {
669                 *ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
670                 WM_gizmo_properties_create(*ptr, gtstring);
671         }
672
673         (*ptr)->data = *properties;
674
675 }
676
677 void WM_gizmo_properties_sanitize(PointerRNA *ptr, const bool no_context)
678 {
679         RNA_STRUCT_BEGIN (ptr, prop)
680         {
681                 switch (RNA_property_type(prop)) {
682                         case PROP_ENUM:
683                                 if (no_context)
684                                         RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
685                                 else
686                                         RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
687                                 break;
688                         case PROP_POINTER:
689                         {
690                                 StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
691
692                                 /* recurse into gizmo properties */
693                                 if (RNA_struct_is_a(ptype, &RNA_GizmoProperties)) {
694                                         PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
695                                         WM_gizmo_properties_sanitize(&opptr, no_context);
696                                 }
697                                 break;
698                         }
699                         default:
700                                 break;
701                 }
702         }
703         RNA_STRUCT_END;
704 }
705
706
707 /** set all props to their default,
708  * \param do_update: Only update un-initialized props.
709  *
710  * \note, there's nothing specific to gizmos here.
711  * this could be made a general function.
712  */
713 bool WM_gizmo_properties_default(PointerRNA *ptr, const bool do_update)
714 {
715         bool changed = false;
716         RNA_STRUCT_BEGIN (ptr, prop)
717         {
718                 switch (RNA_property_type(prop)) {
719                         case PROP_POINTER:
720                         {
721                                 StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
722                                 if (ptype != &RNA_Struct) {
723                                         PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
724                                         changed |= WM_gizmo_properties_default(&opptr, do_update);
725                                 }
726                                 break;
727                         }
728                         default:
729                                 if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) {
730                                         if (RNA_property_reset(ptr, prop, -1)) {
731                                                 changed = true;
732                                         }
733                                 }
734                                 break;
735                 }
736         }
737         RNA_STRUCT_END;
738
739         return changed;
740 }
741
742 /* remove all props without PROP_SKIP_SAVE */
743 void WM_gizmo_properties_reset(wmGizmo *gz)
744 {
745         if (gz->ptr->data) {
746                 PropertyRNA *iterprop;
747                 iterprop = RNA_struct_iterator_property(gz->type->srna);
748
749                 RNA_PROP_BEGIN (gz->ptr, itemptr, iterprop)
750                 {
751                         PropertyRNA *prop = itemptr.data;
752
753                         if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
754                                 const char *identifier = RNA_property_identifier(prop);
755                                 RNA_struct_idprops_unset(gz->ptr, identifier);
756                         }
757                 }
758                 RNA_PROP_END;
759         }
760 }
761
762 void WM_gizmo_properties_clear(PointerRNA *ptr)
763 {
764         IDProperty *properties = ptr->data;
765
766         if (properties) {
767                 IDP_ClearProperty(properties);
768         }
769 }
770
771 void WM_gizmo_properties_free(PointerRNA *ptr)
772 {
773         IDProperty *properties = ptr->data;
774
775         if (properties) {
776                 IDP_FreeProperty(properties);
777                 MEM_freeN(properties);
778                 ptr->data = NULL; /* just in case */
779         }
780 }
781
782 /** \} */
783
784 /** \name General Utilities
785  *
786  * \{ */
787
788 bool WM_gizmo_context_check_drawstep(const struct bContext *C, eWM_GizmoFlagMapDrawStep step)
789 {
790         switch (step) {
791                 case WM_GIZMOMAP_DRAWSTEP_2D:
792                 {
793                         break;
794                 }
795                 case WM_GIZMOMAP_DRAWSTEP_3D:
796                 {
797                         wmWindowManager *wm = CTX_wm_manager(C);
798                         if (ED_screen_animation_playing(wm)) {
799                                 return false;
800                         }
801                         break;
802                 }
803         }
804         return true;
805 }
806
807 /** \} */