7ca1f8fd5f2fc6969d8456cd4ad99790e5b2d7d2
[blender.git] / source / blender / windowmanager / manipulators / intern / wm_manipulator.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/manipulators/intern/wm_manipulator.c
27  *  \ingroup wm
28  */
29
30 #include "MEM_guardedalloc.h"
31
32 #include "BLI_listbase.h"
33 #include "BLI_math.h"
34 #include "BLI_string.h"
35 #include "BLI_string_utils.h"
36
37 #include "BKE_context.h"
38
39 #include "GPU_batch.h"
40 #include "GPU_glew.h"
41 #include "GPU_immediate.h"
42
43 #include "RNA_access.h"
44 #include "RNA_define.h"
45
46 #include "BKE_global.h"
47 #include "BKE_main.h"
48 #include "BKE_idprop.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52
53 #include "ED_screen.h"
54 #include "ED_view3d.h"
55
56 #ifdef WITH_PYTHON
57 #include "BPY_extern.h"
58 #endif
59
60 /* only for own init/exit calls (wm_manipulatortype_init/wm_manipulatortype_free) */
61 #include "wm.h"
62
63 /* own includes */
64 #include "wm_manipulator_wmapi.h"
65 #include "wm_manipulator_intern.h"
66
67 static void wm_manipulator_register(
68         wmManipulatorGroup *mgroup, wmManipulator *mpr);
69
70 /**
71  * \note Follow #wm_operator_create convention.
72  */
73 static wmManipulator *wm_manipulator_create(
74         const wmManipulatorType *wt,
75         PointerRNA *properties)
76 {
77         BLI_assert(wt != NULL);
78         BLI_assert(wt->struct_size >= sizeof(wmManipulator));
79
80         wmManipulator *mpr = MEM_callocN(
81                 wt->struct_size + (sizeof(wmManipulatorProperty) * wt->target_property_defs_len), __func__);
82         mpr->type = wt;
83
84         /* initialize properties, either copy or create */
85         mpr->ptr = MEM_callocN(sizeof(PointerRNA), "wmManipulatorPtrRNA");
86         if (properties && properties->data) {
87                 mpr->properties = IDP_CopyProperty(properties->data);
88         }
89         else {
90                 IDPropertyTemplate val = {0};
91                 mpr->properties = IDP_New(IDP_GROUP, &val, "wmManipulatorProperties");
92         }
93         RNA_pointer_create(G.main->wm.first, wt->srna, mpr->properties, mpr->ptr);
94
95         WM_manipulator_properties_sanitize(mpr->ptr, 0);
96
97         unit_m4(mpr->matrix_space);
98         unit_m4(mpr->matrix_basis);
99         unit_m4(mpr->matrix_offset);
100
101         return mpr;
102 }
103
104 wmManipulator *WM_manipulator_new_ptr(
105         const wmManipulatorType *wt, wmManipulatorGroup *mgroup,
106         PointerRNA *properties)
107 {
108         wmManipulator *mpr = wm_manipulator_create(wt, properties);
109
110         wm_manipulator_register(mgroup, mpr);
111
112         if (mpr->type->setup != NULL) {
113                 mpr->type->setup(mpr);
114         }
115
116         return mpr;
117 }
118
119 /**
120  * \param wt: Must be valid,
121  * if you need to check it exists use #WM_manipulator_new_ptr
122  * because callers of this function don't NULL check the return value.
123  */
124 wmManipulator *WM_manipulator_new(
125         const char *idname, wmManipulatorGroup *mgroup,
126         PointerRNA *properties)
127 {
128         const wmManipulatorType *wt = WM_manipulatortype_find(idname, false);
129         return WM_manipulator_new_ptr(wt, mgroup, properties);
130 }
131
132 /**
133  * Initialize default values and allocate needed memory for members.
134  */
135 static void manipulator_init(wmManipulator *mpr)
136 {
137         const float color_default[4] = {1.0f, 1.0f, 1.0f, 1.0f};
138
139         mpr->scale_basis = 1.0f;
140         mpr->line_width = 1.0f;
141
142         /* defaults */
143         copy_v4_v4(mpr->color, color_default);
144         copy_v4_v4(mpr->color_hi, color_default);
145 }
146
147 /**
148  * Register \a manipulator.
149  *
150  * \param name: name used to create a unique idname for \a manipulator in \a mgroup
151  *
152  * \note Not to be confused with type registration from RNA.
153  */
154 static void wm_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr)
155 {
156         manipulator_init(mpr);
157         wm_manipulatorgroup_manipulator_register(mgroup, mpr);
158 }
159
160 /**
161  * \warning this doesn't check #wmManipulatorMap (highlight, selection etc).
162  * Typical use is when freeing the windowing data,
163  * where caller can manage clearing selection, highlight... etc.
164  */
165 void WM_manipulator_free(wmManipulator *mpr)
166 {
167 #ifdef WITH_PYTHON
168         if (mpr->py_instance) {
169                 /* do this first in case there are any __del__ functions or
170                  * similar that use properties */
171                 BPY_DECREF_RNA_INVALIDATE(mpr->py_instance);
172         }
173 #endif
174
175         if (mpr->op_data) {
176                 for (int i = 0; i < mpr->op_data_len; i++) {
177                         WM_operator_properties_free(&mpr->op_data[i].ptr);
178                 }
179                 MEM_freeN(mpr->op_data);
180         }
181
182         if (mpr->ptr != NULL) {
183                 WM_manipulator_properties_free(mpr->ptr);
184                 MEM_freeN(mpr->ptr);
185         }
186
187         if (mpr->type->target_property_defs_len != 0) {
188                 wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr);
189                 for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
190                         wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
191                         if (mpr_prop->custom_func.free_fn) {
192                                 mpr_prop->custom_func.free_fn(mpr, mpr_prop);
193                         }
194                 }
195         }
196
197         MEM_freeN(mpr);
198 }
199
200 /**
201  * Free \a manipulator and unlink from \a manipulatorlist.
202  * \a manipulatorlist is allowed to be NULL.
203  */
204 void WM_manipulator_unlink(ListBase *manipulatorlist, wmManipulatorMap *mmap, wmManipulator *mpr, bContext *C)
205 {
206         if (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) {
207                 wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
208         }
209         if (mpr->state & WM_MANIPULATOR_STATE_MODAL) {
210                 wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false);
211         }
212         /* Unlink instead of setting so we don't run callbacks. */
213         if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
214                 WM_manipulator_select_unlink(mmap, mpr);
215         }
216
217         if (manipulatorlist) {
218                 BLI_remlink(manipulatorlist, mpr);
219         }
220
221         BLI_assert(mmap->mmap_context.highlight != mpr);
222         BLI_assert(mmap->mmap_context.modal != mpr);
223
224         WM_manipulator_free(mpr);
225 }
226
227 /* -------------------------------------------------------------------- */
228 /** \name Manipulator Creation API
229  *
230  * API for defining data on manipulator creation.
231  *
232  * \{ */
233
234 struct wmManipulatorOpElem *WM_manipulator_operator_get(
235         wmManipulator *mpr, int part_index)
236 {
237         if (mpr->op_data && ((part_index >= 0) && (part_index < mpr->op_data_len))) {
238                 return &mpr->op_data[part_index];
239         }
240         return NULL;
241 }
242
243 PointerRNA *WM_manipulator_operator_set(
244         wmManipulator *mpr, int part_index,
245         wmOperatorType *ot, IDProperty *properties)
246 {
247         BLI_assert(part_index < 255);
248         /* We could pre-allocate these but using multiple is such a rare thing. */
249         if (part_index >= mpr->op_data_len) {
250                 mpr->op_data_len = part_index + 1;
251                 mpr->op_data = MEM_recallocN(mpr->op_data, sizeof(*mpr->op_data) * mpr->op_data_len);
252         }
253         wmManipulatorOpElem *mpop = &mpr->op_data[part_index];
254         mpop->type = ot;
255
256         if (mpop->ptr.data) {
257                 WM_operator_properties_free(&mpop->ptr);
258         }
259         WM_operator_properties_create_ptr(&mpop->ptr, ot);
260
261         if (properties) {
262                 mpop->ptr.data = properties;
263         }
264
265         return &mpop->ptr;
266 }
267
268 static void wm_manipulator_set_matrix_rotation_from_z_axis__internal(
269         float matrix[4][4], const float z_axis[3])
270 {
271         /* old code, seems we can use simpler method */
272 #if 0
273         const float z_global[3] = {0.0f, 0.0f, 1.0f};
274         float rot[3][3];
275
276         rotation_between_vecs_to_mat3(rot, z_global, z_axis);
277         copy_v3_v3(matrix[0], rot[0]);
278         copy_v3_v3(matrix[1], rot[1]);
279         copy_v3_v3(matrix[2], rot[2]);
280 #else
281         normalize_v3_v3(matrix[2], z_axis);
282         ortho_basis_v3v3_v3(matrix[0], matrix[1], matrix[2]);
283 #endif
284
285 }
286
287 static void wm_manipulator_set_matrix_rotation_from_yz_axis__internal(
288         float matrix[4][4], const float y_axis[3], const float z_axis[3])
289 {
290         normalize_v3_v3(matrix[1], y_axis);
291         normalize_v3_v3(matrix[2], z_axis);
292         cross_v3_v3v3(matrix[0], matrix[1], matrix[2]);
293         normalize_v3(matrix[0]);
294 }
295
296 /**
297  * wmManipulator.matrix utils.
298  */
299 void WM_manipulator_set_matrix_rotation_from_z_axis(
300         wmManipulator *mpr, const float z_axis[3])
301 {
302         wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_basis, z_axis);
303 }
304 void WM_manipulator_set_matrix_rotation_from_yz_axis(
305         wmManipulator *mpr, const float y_axis[3], const float z_axis[3])
306 {
307         wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_basis, y_axis, z_axis);
308 }
309 void WM_manipulator_set_matrix_location(wmManipulator *mpr, const float origin[3])
310 {
311         copy_v3_v3(mpr->matrix_basis[3], origin);
312 }
313
314 /**
315  * wmManipulator.matrix_offset utils.
316  */
317 void WM_manipulator_set_matrix_offset_rotation_from_z_axis(
318         wmManipulator *mpr, const float z_axis[3])
319 {
320         wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_offset, z_axis);
321 }
322 void WM_manipulator_set_matrix_offset_rotation_from_yz_axis(
323         wmManipulator *mpr, const float y_axis[3], const float z_axis[3])
324 {
325         wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_offset, y_axis, z_axis);
326 }
327 void WM_manipulator_set_matrix_offset_location(wmManipulator *mpr, const float offset[3])
328 {
329         copy_v3_v3(mpr->matrix_offset[3], offset);
330 }
331
332 void WM_manipulator_set_flag(wmManipulator *mpr, const int flag, const bool enable)
333 {
334         if (enable) {
335                 mpr->flag |= flag;
336         }
337         else {
338                 mpr->flag &= ~flag;
339         }
340 }
341
342 void WM_manipulator_set_scale(wmManipulator *mpr, const float scale)
343 {
344         mpr->scale_basis = scale;
345 }
346
347 void WM_manipulator_set_line_width(wmManipulator *mpr, const float line_width)
348 {
349         mpr->line_width = line_width;
350 }
351
352 /**
353  * Set manipulator rgba colors.
354  *
355  * \param col  Normal state color.
356  * \param col_hi  Highlighted state color.
357  */
358 void WM_manipulator_get_color(const wmManipulator *mpr, float color[4])
359 {
360         copy_v4_v4(color, mpr->color);
361 }
362 void WM_manipulator_set_color(wmManipulator *mpr, const float color[4])
363 {
364         copy_v4_v4(mpr->color, color);
365 }
366
367 void WM_manipulator_get_color_highlight(const wmManipulator *mpr, float color_hi[4])
368 {
369         copy_v4_v4(color_hi, mpr->color_hi);
370 }
371 void WM_manipulator_set_color_highlight(wmManipulator *mpr, const float color_hi[4])
372 {
373         copy_v4_v4(mpr->color_hi, color_hi);
374 }
375
376
377 /** \} */ // Manipulator Creation API
378
379
380 /* -------------------------------------------------------------------- */
381 /** \name Manipulator Callback Assignment
382  *
383  * \{ */
384
385 void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn)
386 {
387         mpr->custom_modal = fn;
388 }
389
390 /** \} */
391
392
393 /* -------------------------------------------------------------------- */
394
395 /**
396  * Add/Remove \a manipulator to selection.
397  * Reallocates memory for selected manipulators so better not call for selecting multiple ones.
398  *
399  * \return if the selection has changed.
400  */
401 bool wm_manipulator_select_set_ex(
402         wmManipulatorMap *mmap, wmManipulator *mpr, bool select,
403         bool use_array, bool use_callback)
404 {
405         bool changed = false;
406
407         if (select) {
408                 if ((mpr->state & WM_MANIPULATOR_STATE_SELECT) == 0) {
409                         if (use_array) {
410                                 wm_manipulatormap_select_array_push_back(mmap, mpr);
411                         }
412                         mpr->state |= WM_MANIPULATOR_STATE_SELECT;
413                         changed = true;
414                 }
415         }
416         else {
417                 if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
418                         if (use_array) {
419                                 wm_manipulatormap_select_array_remove(mmap, mpr);
420                         }
421                         mpr->state &= ~WM_MANIPULATOR_STATE_SELECT;
422                         changed = true;
423                 }
424         }
425
426         /* In the case of unlinking we only want to remove from the array
427          * and not write to the external state */
428         if (use_callback && changed) {
429                 if (mpr->type->select_refresh) {
430                         mpr->type->select_refresh(mpr);
431                 }
432         }
433
434         return changed;
435 }
436
437 /* Remove from selection array without running callbacks. */
438 bool WM_manipulator_select_unlink(wmManipulatorMap *mmap, wmManipulator *mpr)
439 {
440         return wm_manipulator_select_set_ex(mmap, mpr, false, true, false);
441 }
442
443 bool WM_manipulator_select_set(wmManipulatorMap *mmap, wmManipulator *mpr, bool select)
444 {
445         return wm_manipulator_select_set_ex(mmap, mpr, select, true, true);
446 }
447
448 void WM_manipulator_highlight_set(wmManipulatorMap *mmap, wmManipulator *mpr)
449 {
450         wm_manipulatormap_highlight_set(mmap, NULL, mpr, mpr ? mpr->highlight_part : 0);
451 }
452
453 bool wm_manipulator_select_and_highlight(bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr)
454 {
455         if (WM_manipulator_select_set(mmap, mpr, true)) {
456                 wm_manipulatormap_highlight_set(mmap, C, mpr, mpr->highlight_part);
457                 return true;
458         }
459         else {
460                 return false;
461         }
462 }
463
464 void wm_manipulator_calculate_scale(wmManipulator *mpr, const bContext *C)
465 {
466         const RegionView3D *rv3d = CTX_wm_region_view3d(C);
467         float scale = U.ui_scale;
468
469         if ((mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCALE) == 0) {
470                 scale *= U.manipulator_size;
471                 if (rv3d) {
472                         /* 'ED_view3d_pixel_size' includes 'U.pixelsize', remove it. */
473                         float matrix_world[4][4];
474                         if (mpr->type->matrix_basis_get) {
475                                 float matrix_basis[4][4];
476                                 mpr->type->matrix_basis_get(mpr, matrix_basis);
477                                 mul_m4_m4m4(matrix_world, mpr->matrix_space, matrix_basis);
478                         }
479                         else {
480                                 mul_m4_m4m4(matrix_world, mpr->matrix_space, mpr->matrix_basis);
481                         }
482
483                         /* Exclude matrix_offset from scale. */
484                         scale *= ED_view3d_pixel_size(rv3d, matrix_world[3]) / U.pixelsize;
485                 }
486                 else {
487                         scale *= 0.02f;
488                 }
489         }
490
491         mpr->scale_final = mpr->scale_basis * scale;
492 }
493
494 static void manipulator_update_prop_data(wmManipulator *mpr)
495 {
496         /* manipulator property might have been changed, so update manipulator */
497         if (mpr->type->property_update) {
498                 wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr);
499                 for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
500                         wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
501                         if (WM_manipulator_target_property_is_valid(mpr_prop)) {
502                                 mpr->type->property_update(mpr, mpr_prop);
503                         }
504                 }
505         }
506 }
507
508 void wm_manipulator_update(wmManipulator *mpr, const bContext *C, const bool refresh_map)
509 {
510         if (refresh_map) {
511                 manipulator_update_prop_data(mpr);
512         }
513         wm_manipulator_calculate_scale(mpr, C);
514 }
515
516 int wm_manipulator_is_visible(wmManipulator *mpr)
517 {
518         if (mpr->flag & WM_MANIPULATOR_HIDDEN) {
519                 return 0;
520         }
521         if ((mpr->state & WM_MANIPULATOR_STATE_MODAL) &&
522             !(mpr->flag & (WM_MANIPULATOR_DRAW_MODAL | WM_MANIPULATOR_DRAW_VALUE)))
523         {
524                 /* don't draw while modal (dragging) */
525                 return 0;
526         }
527         if ((mpr->flag & WM_MANIPULATOR_DRAW_HOVER) &&
528             !(mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) &&
529             !(mpr->state & WM_MANIPULATOR_STATE_SELECT)) /* still draw selected manipulators */
530         {
531                 /* update but don't draw */
532                 return WM_MANIPULATOR_IS_VISIBLE_UPDATE;
533         }
534
535         return WM_MANIPULATOR_IS_VISIBLE_UPDATE | WM_MANIPULATOR_IS_VISIBLE_DRAW;
536 }
537
538 void WM_manipulator_calc_matrix_final_params(
539         const wmManipulator *mpr,
540         const struct WM_ManipulatorMatrixParams *params,
541         float r_mat[4][4])
542 {
543         const float (* const matrix_space)[4]  = params->matrix_space  ? params->matrix_space  : mpr->matrix_space;
544         const float (* const matrix_basis)[4]  = params->matrix_basis  ? params->matrix_basis  : mpr->matrix_basis;
545         const float (* const matrix_offset)[4] = params->matrix_offset ? params->matrix_offset : mpr->matrix_offset;
546         const float *scale_final = params->scale_final ? params->scale_final : &mpr->scale_final;
547
548         float final_matrix[4][4];
549         if (params->matrix_basis == NULL && mpr->type->matrix_basis_get) {
550                 mpr->type->matrix_basis_get(mpr, final_matrix);
551         }
552         else {
553                 copy_m4_m4(final_matrix, matrix_basis);
554         }
555
556         if (mpr->flag & WM_MANIPULATOR_DRAW_NO_SCALE) {
557                 mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
558         }
559         else {
560                 if (mpr->flag & WM_MANIPULATOR_DRAW_OFFSET_SCALE) {
561                         mul_mat3_m4_fl(final_matrix, *scale_final);
562                         mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
563                 }
564                 else {
565                         mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
566                         mul_mat3_m4_fl(final_matrix, *scale_final);
567                 }
568         }
569
570         mul_m4_m4m4(r_mat, matrix_space, final_matrix);
571 }
572
573 void WM_manipulator_calc_matrix_final(const wmManipulator *mpr, float r_mat[4][4])
574 {
575         WM_manipulator_calc_matrix_final_params(
576                 mpr,
577                 &((struct WM_ManipulatorMatrixParams) {
578                     .matrix_space = NULL,
579                     .matrix_basis = NULL,
580                     .matrix_offset = NULL,
581                     .scale_final = NULL,
582                 }), r_mat
583         );
584 }
585
586 /** \name Manipulator Propery Access
587  *
588  * Matches `WM_operator_properties` conventions.
589  *
590  * \{ */
591
592
593 void WM_manipulator_properties_create_ptr(PointerRNA *ptr, wmManipulatorType *wt)
594 {
595         RNA_pointer_create(NULL, wt->srna, NULL, ptr);
596 }
597
598 void WM_manipulator_properties_create(PointerRNA *ptr, const char *wtstring)
599 {
600         const wmManipulatorType *wt = WM_manipulatortype_find(wtstring, false);
601
602         if (wt)
603                 WM_manipulator_properties_create_ptr(ptr, (wmManipulatorType *)wt);
604         else
605                 RNA_pointer_create(NULL, &RNA_ManipulatorProperties, NULL, ptr);
606 }
607
608 /* similar to the function above except its uses ID properties
609  * used for keymaps and macros */
610 void WM_manipulator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *wtstring)
611 {
612         if (*properties == NULL) {
613                 IDPropertyTemplate val = {0};
614                 *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
615         }
616
617         if (*ptr == NULL) {
618                 *ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
619                 WM_manipulator_properties_create(*ptr, wtstring);
620         }
621
622         (*ptr)->data = *properties;
623
624 }
625
626 void WM_manipulator_properties_sanitize(PointerRNA *ptr, const bool no_context)
627 {
628         RNA_STRUCT_BEGIN (ptr, prop)
629         {
630                 switch (RNA_property_type(prop)) {
631                         case PROP_ENUM:
632                                 if (no_context)
633                                         RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
634                                 else
635                                         RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
636                                 break;
637                         case PROP_POINTER:
638                         {
639                                 StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
640
641                                 /* recurse into manipulator properties */
642                                 if (RNA_struct_is_a(ptype, &RNA_ManipulatorProperties)) {
643                                         PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
644                                         WM_manipulator_properties_sanitize(&opptr, no_context);
645                                 }
646                                 break;
647                         }
648                         default:
649                                 break;
650                 }
651         }
652         RNA_STRUCT_END;
653 }
654
655
656 /** set all props to their default,
657  * \param do_update Only update un-initialized props.
658  *
659  * \note, theres nothing specific to manipulators here.
660  * this could be made a general function.
661  */
662 bool WM_manipulator_properties_default(PointerRNA *ptr, const bool do_update)
663 {
664         bool changed = false;
665         RNA_STRUCT_BEGIN (ptr, prop)
666         {
667                 switch (RNA_property_type(prop)) {
668                         case PROP_POINTER:
669                         {
670                                 StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
671                                 if (ptype != &RNA_Struct) {
672                                         PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
673                                         changed |= WM_manipulator_properties_default(&opptr, do_update);
674                                 }
675                                 break;
676                         }
677                         default:
678                                 if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) {
679                                         if (RNA_property_reset(ptr, prop, -1)) {
680                                                 changed = true;
681                                         }
682                                 }
683                                 break;
684                 }
685         }
686         RNA_STRUCT_END;
687
688         return changed;
689 }
690
691 /* remove all props without PROP_SKIP_SAVE */
692 void WM_manipulator_properties_reset(wmManipulator *mpr)
693 {
694         if (mpr->ptr->data) {
695                 PropertyRNA *iterprop;
696                 iterprop = RNA_struct_iterator_property(mpr->type->srna);
697
698                 RNA_PROP_BEGIN (mpr->ptr, itemptr, iterprop)
699                 {
700                         PropertyRNA *prop = itemptr.data;
701
702                         if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
703                                 const char *identifier = RNA_property_identifier(prop);
704                                 RNA_struct_idprops_unset(mpr->ptr, identifier);
705                         }
706                 }
707                 RNA_PROP_END;
708         }
709 }
710
711 void WM_manipulator_properties_clear(PointerRNA *ptr)
712 {
713         IDProperty *properties = ptr->data;
714
715         if (properties) {
716                 IDP_ClearProperty(properties);
717         }
718 }
719
720 void WM_manipulator_properties_free(PointerRNA *ptr)
721 {
722         IDProperty *properties = ptr->data;
723
724         if (properties) {
725                 IDP_FreeProperty(properties);
726                 MEM_freeN(properties);
727                 ptr->data = NULL; /* just in case */
728         }
729 }
730
731 /** \} */