Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/windowmanager/gizmo')
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_api.h343
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_types.h422
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo.c800
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c949
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c197
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h144
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c1209
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c364
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c212
-rw-r--r--source/blender/windowmanager/gizmo/wm_gizmo_fn.h88
-rw-r--r--source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h97
11 files changed, 4825 insertions, 0 deletions
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
new file mode 100644
index 00000000000..ffd18af496e
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h
@@ -0,0 +1,343 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmos/WM_gizmo_api.h
+ * \ingroup wm
+ *
+ * \name Manipulator API
+ * \brief API for external use of wmManipulator types.
+ *
+ * Only included in WM_api.h
+ */
+
+
+#ifndef __WM_GIZMO_API_H__
+#define __WM_GIZMO_API_H__
+
+struct ARegion;
+struct GHashIterator;
+struct IDProperty;
+struct Main;
+struct PropertyRNA;
+struct wmKeyConfig;
+struct wmManipulator;
+struct wmManipulatorProperty;
+struct wmManipulatorPropertyType;
+struct wmManipulatorType;
+struct wmManipulatorGroup;
+struct wmManipulatorGroupType;
+struct wmManipulatorMap;
+struct wmManipulatorMapType;
+struct wmManipulatorMapType_Params;
+struct wmMsgSubscribeKey;
+struct wmMsgSubscribeValue;
+
+#include "wm_gizmo_fn.h"
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+struct wmManipulator *WM_manipulator_new_ptr(
+ const struct wmManipulatorType *wt, struct wmManipulatorGroup *mgroup,
+ struct PointerRNA *properties);
+struct wmManipulator *WM_manipulator_new(
+ const char *idname, struct wmManipulatorGroup *mgroup,
+ struct PointerRNA *properties);
+void WM_manipulator_free(struct wmManipulator *mpr);
+void WM_manipulator_unlink(
+ ListBase *manipulatorlist, struct wmManipulatorMap *mmap, struct wmManipulator *mpr,
+ struct bContext *C);
+
+void WM_manipulator_name_set(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr, const char *name);
+
+bool WM_manipulator_select_unlink(struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
+bool WM_manipulator_select_set(struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select);
+void WM_manipulator_highlight_set(struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
+
+void WM_manipulator_modal_set_from_setup(
+ struct wmManipulatorMap *mmap, struct bContext *C,
+ struct wmManipulator *mpr, int part_index, const struct wmEvent *event);
+
+struct wmManipulatorOpElem *WM_manipulator_operator_get(
+ struct wmManipulator *mpr, int part_index);
+struct PointerRNA *WM_manipulator_operator_set(
+ struct wmManipulator *mpr, int part_index,
+ struct wmOperatorType *ot, struct IDProperty *properties);
+
+/* callbacks */
+void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn);
+
+void WM_manipulator_set_matrix_location(
+ struct wmManipulator *mpr, const float origin[3]);
+void WM_manipulator_set_matrix_rotation_from_z_axis(
+ struct wmManipulator *mpr, const float z_axis[3]);
+void WM_manipulator_set_matrix_rotation_from_yz_axis(
+ struct wmManipulator *mpr, const float y_axis[3], const float z_axis[3]);
+
+void WM_manipulator_set_matrix_offset_location(
+ struct wmManipulator *mpr, const float origin[3]);
+void WM_manipulator_set_matrix_offset_rotation_from_z_axis(
+ struct wmManipulator *mpr, const float z_axis[3]);
+void WM_manipulator_set_matrix_offset_rotation_from_yz_axis(
+ struct wmManipulator *mpr, const float y_axis[3], const float z_axis[3]);
+
+void WM_manipulator_set_flag(struct wmManipulator *mpr, const int flag, const bool enable);
+void WM_manipulator_set_scale(struct wmManipulator *mpr, float scale);
+void WM_manipulator_set_line_width(struct wmManipulator *mpr, const float line_width);
+
+void WM_manipulator_get_color(const struct wmManipulator *mpr, float color[4]);
+void WM_manipulator_set_color(struct wmManipulator *mpr, const float color[4]);
+void WM_manipulator_get_color_highlight(const struct wmManipulator *mpr, float color_hi[4]);
+void WM_manipulator_set_color_highlight(struct wmManipulator *mpr, const float color[4]);
+
+/**
+ * Leaving values NULL use values from #wmManipulator.
+ */
+struct WM_ManipulatorMatrixParams {
+ const float(*matrix_space)[4];
+ const float(*matrix_basis)[4];
+ const float(*matrix_offset)[4];
+ const float *scale_final;
+};
+
+void WM_manipulator_calc_matrix_final_params(
+ const struct wmManipulator *mpr, const struct WM_ManipulatorMatrixParams *params,
+ float r_mat[4][4]);
+void WM_manipulator_calc_matrix_final_no_offset(
+ const struct wmManipulator *mpr, float r_mat[4][4]);
+
+void WM_manipulator_calc_matrix_final(
+ const struct wmManipulator *mpr, float r_mat[4][4]);
+
+/* properties */
+void WM_manipulator_properties_create_ptr(struct PointerRNA *ptr, struct wmManipulatorType *wt);
+void WM_manipulator_properties_create(struct PointerRNA *ptr, const char *opstring);
+void WM_manipulator_properties_alloc(struct PointerRNA **ptr, struct IDProperty **properties, const char *wtstring);
+void WM_manipulator_properties_sanitize(struct PointerRNA *ptr, const bool no_context);
+bool WM_manipulator_properties_default(struct PointerRNA *ptr, const bool do_update);
+void WM_manipulator_properties_reset(struct wmManipulator *op);
+void WM_manipulator_properties_clear(struct PointerRNA *ptr);
+void WM_manipulator_properties_free(struct PointerRNA *ptr);
+
+
+/* wm_manipulator_type.c */
+const struct wmManipulatorType *WM_manipulatortype_find(const char *idname, bool quiet);
+void WM_manipulatortype_append(void (*wtfunc)(struct wmManipulatorType *));
+void WM_manipulatortype_append_ptr(void (*mnpfunc)(struct wmManipulatorType *, void *), void *userdata);
+bool WM_manipulatortype_remove(struct bContext *C, struct Main *bmain, const char *idname);
+void WM_manipulatortype_remove_ptr(struct bContext *C, struct Main *bmain, struct wmManipulatorType *wt);
+void WM_manipulatortype_iter(struct GHashIterator *ghi);
+
+/* wm_manipulator_group_type.c */
+struct wmManipulatorGroupType *WM_manipulatorgrouptype_find(const char *idname, bool quiet);
+struct wmManipulatorGroupType *WM_manipulatorgrouptype_append(void (*wtfunc)(struct wmManipulatorGroupType *));
+struct wmManipulatorGroupType *WM_manipulatorgrouptype_append_ptr(void (*mnpfunc)(struct wmManipulatorGroupType *, void *), void *userdata);
+bool WM_manipulatorgrouptype_free(const char *idname);
+void WM_manipulatorgrouptype_free_ptr(struct wmManipulatorGroupType *wt);
+void WM_manipulatorgrouptype_iter(struct GHashIterator *ghi);
+
+struct wmManipulatorGroupTypeRef *WM_manipulatorgrouptype_append_and_link(
+ struct wmManipulatorMapType *mmap_type,
+ void (*wtfunc)(struct wmManipulatorGroupType *));
+
+/* wm_manipulator_map.c */
+
+/* Dynamic Updates (for RNA runtime registration) */
+void WM_manipulatorconfig_update_tag_init(struct wmManipulatorMapType *mmap_type, struct wmManipulatorGroupType *wgt);
+void WM_manipulatorconfig_update_tag_remove(struct wmManipulatorMapType *mmap_type, struct wmManipulatorGroupType *wgt);
+void WM_manipulatorconfig_update(struct Main *bmain);
+
+
+/* wm_maniulator_target_props.c */
+struct wmManipulatorProperty *WM_manipulator_target_property_array(struct wmManipulator *mpr);
+struct wmManipulatorProperty *WM_manipulator_target_property_at_index(
+ struct wmManipulator *mpr, int index);
+struct wmManipulatorProperty *WM_manipulator_target_property_find(
+ struct wmManipulator *mpr, const char *idname);
+
+void WM_manipulator_target_property_def_rna_ptr(
+ struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type,
+ struct PointerRNA *ptr, struct PropertyRNA *prop, int index);
+void WM_manipulator_target_property_def_rna(
+ struct wmManipulator *mpr, const char *idname,
+ struct PointerRNA *ptr, const char *propname, int index);
+
+void WM_manipulator_target_property_def_func_ptr(
+ struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type,
+ const struct wmManipulatorPropertyFnParams *params);
+void WM_manipulator_target_property_def_func(
+ struct wmManipulator *mpr, const char *idname,
+ const struct wmManipulatorPropertyFnParams *params);
+
+void WM_manipulator_target_property_clear_rna_ptr(
+ struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type);
+void WM_manipulator_target_property_clear_rna(
+ struct wmManipulator *mpr, const char *idname);
+
+bool WM_manipulator_target_property_is_valid_any(struct wmManipulator *mpr);
+bool WM_manipulator_target_property_is_valid(
+ const struct wmManipulatorProperty *mpr_prop);
+float WM_manipulator_target_property_value_get(
+ const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop);
+void WM_manipulator_target_property_value_set(
+ struct bContext *C, const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
+ const float value);
+
+void WM_manipulator_target_property_value_get_array(
+ const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
+ float *value);
+void WM_manipulator_target_property_value_set_array(
+ struct bContext *C, const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
+ const float *value);
+
+bool WM_manipulator_target_property_range_get(
+ const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
+ float range[2]);
+
+int WM_manipulator_target_property_array_length(
+ const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop);
+
+/* definitions */
+const struct wmManipulatorPropertyType *WM_manipulatortype_target_property_find(
+ const struct wmManipulatorType *wt, const char *idname);
+void WM_manipulatortype_target_property_def(
+ struct wmManipulatorType *wt, const char *idname, int data_type, int array_length);
+
+/* utilities */
+void WM_manipulator_do_msg_notify_tag_refresh(
+ struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val);
+void WM_manipulator_target_property_subscribe_all(
+ struct wmManipulator *mpr, struct wmMsgBus *mbus, struct ARegion *ar);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+/* Callbacks for 'wmManipulatorGroupType.setup_keymap' */
+struct wmKeyMap *WM_manipulatorgroup_keymap_common(
+ const struct wmManipulatorGroupType *wgt, struct wmKeyConfig *config);
+struct wmKeyMap *WM_manipulatorgroup_keymap_common_select(
+ const struct wmManipulatorGroupType *wgt, struct wmKeyConfig *config);
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+struct wmManipulatorMap *WM_manipulatormap_new_from_type(
+ const struct wmManipulatorMapType_Params *mmap_params);
+const struct ListBase *WM_manipulatormap_group_list(struct wmManipulatorMap *mmap);
+struct wmManipulatorGroup *WM_manipulatormap_group_find(
+ struct wmManipulatorMap *mmap,
+ const char *idname);
+struct wmManipulatorGroup *WM_manipulatormap_group_find_ptr(
+ struct wmManipulatorMap *mmap,
+ const struct wmManipulatorGroupType *wgt);
+void WM_manipulatormap_tag_refresh(struct wmManipulatorMap *mmap);
+void WM_manipulatormap_draw(
+ struct wmManipulatorMap *mmap, const struct bContext *C, const eWM_ManipulatorMapDrawStep drawstep);
+void WM_manipulatormap_add_handlers(struct ARegion *ar, struct wmManipulatorMap *mmap);
+bool WM_manipulatormap_select_all(struct bContext *C, struct wmManipulatorMap *mmap, const int action);
+bool WM_manipulatormap_cursor_set(const struct wmManipulatorMap *mmap, struct wmWindow *win);
+void WM_manipulatormap_message_subscribe(
+ struct bContext *C, struct wmManipulatorMap *mmap, struct ARegion *ar, struct wmMsgBus *mbus);
+bool WM_manipulatormap_is_any_selected(const struct wmManipulatorMap *mmap);
+bool WM_manipulatormap_minmax(
+ const struct wmManipulatorMap *mmap, bool use_hidden, bool use_select,
+ float r_min[3], float r_max[3]);
+
+struct ARegion *WM_manipulatormap_tooltip_init(
+ struct bContext *C, struct ARegion *ar, bool *r_exit_on_event);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMapType */
+
+struct wmManipulatorMapType *WM_manipulatormaptype_find(
+ const struct wmManipulatorMapType_Params *mmap_params);
+struct wmManipulatorMapType *WM_manipulatormaptype_ensure(
+ const struct wmManipulatorMapType_Params *mmap_params);
+
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find(
+ struct wmManipulatorMapType *mmap_type,
+ const char *idname);
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr(
+ struct wmManipulatorMapType *mmap_type,
+ const struct wmManipulatorGroupType *wgt);
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link(
+ struct wmManipulatorMapType *mmap_type,
+ const char *idname);
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr(
+ struct wmManipulatorMapType *mmap_type,
+ struct wmManipulatorGroupType *wgt);
+
+void WM_manipulatormaptype_group_init_runtime_keymap(
+ const struct Main *bmain,
+ struct wmManipulatorGroupType *wgt);
+void WM_manipulatormaptype_group_init_runtime(
+ const struct Main *bmain, struct wmManipulatorMapType *mmap_type,
+ struct wmManipulatorGroupType *wgt);
+void WM_manipulatormaptype_group_unlink(
+ struct bContext *C, struct Main *bmain, struct wmManipulatorMapType *mmap_type,
+ const struct wmManipulatorGroupType *wgt);
+
+void WM_manipulatormaptype_group_free(struct wmManipulatorGroupTypeRef *wgt);
+
+/* -------------------------------------------------------------------- */
+/* ManipulatorGroup */
+
+/* Add/Ensure/Remove (High level API) */
+
+void WM_manipulator_group_type_add_ptr_ex(
+ struct wmManipulatorGroupType *wgt,
+ struct wmManipulatorMapType *mmap_type);
+void WM_manipulator_group_type_add_ptr(
+ struct wmManipulatorGroupType *wgt);
+void WM_manipulator_group_type_add(const char *idname);
+
+void WM_manipulator_group_type_ensure_ptr_ex(
+ struct wmManipulatorGroupType *wgt,
+ struct wmManipulatorMapType *mmap_type);
+void WM_manipulator_group_type_ensure_ptr(
+ struct wmManipulatorGroupType *wgt);
+void WM_manipulator_group_type_ensure(const char *idname);
+
+void WM_manipulator_group_type_remove_ptr_ex(
+ struct Main *bmain, struct wmManipulatorGroupType *wgt,
+ struct wmManipulatorMapType *mmap_type);
+void WM_manipulator_group_type_remove_ptr(
+ struct Main *bmain, struct wmManipulatorGroupType *wgt);
+void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname);
+
+void WM_manipulator_group_type_unlink_delayed_ptr_ex(
+ struct wmManipulatorGroupType *wgt,
+ struct wmManipulatorMapType *mmap_type);
+void WM_manipulator_group_type_unlink_delayed_ptr(
+ struct wmManipulatorGroupType *wgt);
+void WM_manipulator_group_type_unlink_delayed(const char *idname);
+
+/* Utilities */
+bool WM_manipulator_context_check_drawstep(const struct bContext *C, eWM_ManipulatorMapDrawStep step);
+
+bool WM_manipulator_group_type_poll(const struct bContext *C, const struct wmManipulatorGroupType *wgt);
+
+#endif /* __WM_GIZMO_API_H__ */
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
new file mode 100644
index 00000000000..0f6af6db24c
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
@@ -0,0 +1,422 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmo/WM_gizmo_types.h
+ * \ingroup wm
+ *
+ * \name Manipulator Types
+ * \brief Manipulator defines for external use.
+ *
+ * Only included in WM_types.h and lower level files.
+ */
+
+
+#ifndef __WM_GIZMO_TYPES_H__
+#define __WM_GIZMO_TYPES_H__
+
+#include "BLI_compiler_attrs.h"
+
+struct wmManipulatorMapType;
+struct wmManipulatorGroupType;
+struct wmManipulatorGroup;
+struct wmManipulator;
+struct wmManipulatorProperty;
+struct wmKeyConfig;
+
+#include "DNA_listBase.h"
+
+
+/* -------------------------------------------------------------------- */
+/* Enum Typedef's */
+
+
+/**
+ * #wmManipulator.state
+ */
+typedef enum eWM_ManipulatorState {
+ WM_MANIPULATOR_STATE_HIGHLIGHT = (1 << 0), /* while hovered */
+ WM_MANIPULATOR_STATE_MODAL = (1 << 1), /* while dragging */
+ WM_MANIPULATOR_STATE_SELECT = (1 << 2),
+} eWM_ManipulatorState;
+
+
+/**
+ * #wmManipulator.flag
+ * Flags for individual manipulators.
+ */
+typedef enum eWM_ManipulatorFlag {
+ WM_MANIPULATOR_DRAW_HOVER = (1 << 0), /* draw *only* while hovering */
+ WM_MANIPULATOR_DRAW_MODAL = (1 << 1), /* draw while dragging */
+ WM_MANIPULATOR_DRAW_VALUE = (1 << 2), /* draw an indicator for the current value while dragging */
+ WM_MANIPULATOR_HIDDEN = (1 << 3),
+ /**
+ * When set 'scale_final' value also scales the offset.
+ * Use when offset is to avoid screen-space overlap instead of absolute positioning. */
+ WM_MANIPULATOR_DRAW_OFFSET_SCALE = (1 << 4),
+ /**
+ * User should still use 'scale_final' for any handles and UI elements.
+ * This simply skips scale when calculating the final matrix.
+ * Needed when the manipulator needs to align with the interface underneath it. */
+ WM_MANIPULATOR_DRAW_NO_SCALE = (1 << 5),
+ /**
+ * Hide the cursor and lock it's position while interacting with this manipulator.
+ */
+ WM_MANIPULATOR_GRAB_CURSOR = (1 << 6),
+ /** Don't write into the depth buffer when selecting. */
+ WM_MANIPULATOR_SELECT_BACKGROUND = (1 << 7),
+} eWM_ManipulatorFlag;
+
+/**
+ * #wmManipulatorGroupType.flag
+ * Flags that influence the behavior of all manipulators in the group.
+ */
+typedef enum eWM_ManipulatorGroupTypeFlag {
+ /* Mark manipulator-group as being 3D */
+ WM_MANIPULATORGROUPTYPE_3D = (1 << 0),
+ /* Scale manipulators as 3D object that respects zoom (otherwise zoom independent draw size).
+ * note: currently only for 3D views, 2D support needs adding. */
+ WM_MANIPULATORGROUPTYPE_SCALE = (1 << 1),
+ /* Manipulators can be depth culled with scene objects (covered by other geometry - TODO) */
+ WM_MANIPULATORGROUPTYPE_DEPTH_3D = (1 << 2),
+ /* Manipulators can be selected */
+ WM_MANIPULATORGROUPTYPE_SELECT = (1 << 3),
+ /* The manipulator group is to be kept (not removed on loading a new file for eg). */
+ WM_MANIPULATORGROUPTYPE_PERSISTENT = (1 << 4),
+ /* Show all other manipulators when interacting. */
+ WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL = (1 << 5),
+} eWM_ManipulatorGroupTypeFlag;
+
+
+/**
+ * #wmManipulatorGroup.init_flag
+ */
+typedef enum eWM_ManipulatorGroupInitFlag {
+ /* mgroup has been initialized */
+ WM_MANIPULATORGROUP_INIT_SETUP = (1 << 0),
+ WM_MANIPULATORGROUP_INIT_REFRESH = (1 << 1),
+} eWM_ManipulatorGroupInitFlag;
+
+/**
+ * #wmManipulatorMapType.type_update_flag
+ * Manipulator-map type update flag
+ */
+typedef enum eWM_ManipulatorMapTypeUpdateFlag {
+ /* A new type has been added, needs to be initialized for all views. */
+ WM_MANIPULATORMAPTYPE_UPDATE_INIT = (1 << 0),
+ WM_MANIPULATORMAPTYPE_UPDATE_REMOVE = (1 << 1),
+
+ /* Needed because keymap may be registered before and after window initialization.
+ * So we need to keep track of keymap initialization separately. */
+ WM_MANIPULATORMAPTYPE_KEYMAP_INIT = (1 << 2),
+} eWM_ManipulatorMapTypeUpdateFlag;
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+/**
+ * \brief Manipulator tweak flag.
+ * Bitflag passed to manipulator while tweaking.
+ *
+ * \note Manipulators are responsible for handling this #wmManipulator.modal callback!.
+ */
+typedef enum {
+ /* Drag with extra precision (Shift). */
+ WM_MANIPULATOR_TWEAK_PRECISE = (1 << 0),
+ /* Drag with snap enabled (Ctrl). */
+ WM_MANIPULATOR_TWEAK_SNAP = (1 << 1),
+} eWM_ManipulatorTweak;
+
+#include "wm_gizmo_fn.h"
+
+typedef struct wmManipulatorOpElem {
+ struct wmOperatorType *type;
+ /* operator properties if manipulator spawns and controls an operator,
+ * or owner pointer if manipulator spawns and controls a property */
+ PointerRNA ptr;
+
+ bool is_redo;
+} wmManipulatorOpElem;
+
+/* manipulators are set per region by registering them on manipulator-maps */
+struct wmManipulator {
+ struct wmManipulator *next, *prev;
+
+ /* While we don't have a real type, use this to put type-like vars. */
+ const struct wmManipulatorType *type;
+
+ /* Overrides 'type->modal' when set.
+ * Note that this is a workaround, remove if we can. */
+ wmManipulatorFnModal custom_modal;
+
+ /* pointer back to group this manipulator is in (just for quick access) */
+ struct wmManipulatorGroup *parent_mgroup;
+
+ void *py_instance;
+
+ /* rna pointer to access properties */
+ struct PointerRNA *ptr;
+
+ /* flags that influence the behavior or how the manipulators are drawn */
+ eWM_ManipulatorFlag flag;
+ /* state flags (active, highlighted, selected) */
+ eWM_ManipulatorState state;
+
+ /* Optional ID for highlighting different parts of this manipulator.
+ * -1 when unset, otherwise a valid index. (Used as index to 'op_data'). */
+ int highlight_part;
+ /* For single click button manipulators, use a different part as a fallback, -1 when unused. */
+ int drag_part;
+
+ /* Transformation of the manipulator in 2d or 3d space.
+ * - Matrix axis are expected to be unit length (scale is applied after).
+ * - Behavior when axis aren't orthogonal depends on each manipulator.
+ * - Typically the +Z is the primary axis for manipulators to use.
+ * - 'matrix[3]' must be used for location,
+ * besides this it's up to the manipulators internal code how the
+ * rotation components are used for drawing and interaction.
+ */
+
+ /* The space this manipulator is being modified in. */
+ float matrix_space[4][4];
+ /* Transformation of this manipulator. */
+ float matrix_basis[4][4];
+ /* custom offset from origin */
+ float matrix_offset[4][4];
+ /* runtime property, set the scale while drawing on the viewport */
+ float scale_final;
+ /* user defined scale, in addition to the original one */
+ float scale_basis;
+ /* user defined width for line drawing */
+ float line_width;
+ /* manipulator colors (uses default fallbacks if not defined) */
+ float color[4], color_hi[4];
+
+ /* data used during interaction */
+ void *interaction_data;
+
+ /* Operator to spawn when activating the manipulator (overrides property editing),
+ * an array of items (aligned with #wmManipulator.highlight_part). */
+ wmManipulatorOpElem *op_data;
+ int op_data_len;
+
+ struct IDProperty *properties;
+
+ /* over alloc target_properties after 'wmManipulatorType.struct_size' */
+};
+
+/* Similar to PropertyElemRNA, but has an identifier. */
+typedef struct wmManipulatorProperty {
+ const struct wmManipulatorPropertyType *type;
+
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ int index;
+
+
+ /* Optional functions for converting to/from RNA */
+ struct {
+ wmManipulatorPropertyFnGet value_get_fn;
+ wmManipulatorPropertyFnSet value_set_fn;
+ wmManipulatorPropertyFnRangeGet range_get_fn;
+ wmManipulatorPropertyFnFree free_fn;
+ void *user_data;
+ } custom_func;
+} wmManipulatorProperty;
+
+typedef struct wmManipulatorPropertyType {
+ struct wmManipulatorPropertyType *next, *prev;
+ /* PropertyType, typically 'PROP_FLOAT' */
+ int data_type;
+ int array_length;
+
+ /* index within 'wmManipulatorType' */
+ int index_in_type;
+
+ /* over alloc */
+ char idname[0];
+} wmManipulatorPropertyType;
+
+
+/**
+ * Simple utility wrapper for storing a single manipulator as wmManipulatorGroup.customdata (which gets freed).
+ */
+typedef struct wmManipulatorWrapper {
+ struct wmManipulator *manipulator;
+} wmManipulatorWrapper;
+
+struct wmManipulatorMapType_Params {
+ short spaceid;
+ short regionid;
+};
+
+typedef struct wmManipulatorType {
+
+ const char *idname; /* MAX_NAME */
+
+ /* Set to 'sizeof(wmManipulator)' or larger for instances of this type,
+ * use so we can cant to other types without the hassle of a custom-data pointer. */
+ uint struct_size;
+
+ /* Initialize struct (calloc'd 'struct_size' region). */
+ wmManipulatorFnSetup setup;
+
+ /* draw manipulator */
+ wmManipulatorFnDraw draw;
+
+ /* determines 3d intersection by rendering the manipulator in a selection routine. */
+ wmManipulatorFnDrawSelect draw_select;
+
+ /* Determine if the mouse intersects with the manipulator.
+ * The calculation should be done in the callback itself, -1 for no seleciton. */
+ wmManipulatorFnTestSelect test_select;
+
+ /* handler used by the manipulator. Usually handles interaction tied to a manipulator type */
+ wmManipulatorFnModal modal;
+
+ /* manipulator-specific handler to update manipulator attributes based on the property value */
+ wmManipulatorFnPropertyUpdate property_update;
+
+ /* Returns the final transformation which may be different from the 'matrix',
+ * depending on the manipulator.
+ * Notes:
+ * - Scale isn't applied (wmManipulator.scale/user_scale).
+ * - Offset isn't applied (wmManipulator.matrix_offset).
+ */
+ wmManipulatorFnMatrixBasisGet matrix_basis_get;
+
+ /* activate a manipulator state when the user clicks on it */
+ wmManipulatorFnInvoke invoke;
+
+ /* called when manipulator tweaking is done - used to free data and reset property when cancelling */
+ wmManipulatorFnExit exit;
+
+ wmManipulatorFnCursorGet cursor_get;
+
+ /* called when manipulator selection state changes */
+ wmManipulatorFnSelectRefresh select_refresh;
+
+ /* Free data (not the manipulator it's self), use when the manipulator allocates it's own members. */
+ wmManipulatorFnFree free;
+
+ /* RNA for properties */
+ struct StructRNA *srna;
+
+ /* RNA integration */
+ ExtensionRNA ext;
+
+ ListBase target_property_defs;
+ int target_property_defs_len;
+
+} wmManipulatorType;
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+/* factory class for a manipulator-group type, gets called every time a new area is spawned */
+typedef struct wmManipulatorGroupTypeRef {
+ struct wmManipulatorGroupTypeRef *next, *prev;
+ struct wmManipulatorGroupType *type;
+} wmManipulatorGroupTypeRef;
+
+/* factory class for a manipulator-group type, gets called every time a new area is spawned */
+typedef struct wmManipulatorGroupType {
+ const char *idname; /* MAX_NAME */
+ const char *name; /* manipulator-group name - displayed in UI (keymap editor) */
+ char owner_id[64]; /* MAX_NAME */
+
+ /* poll if manipulator-map should be visible */
+ wmManipulatorGroupFnPoll poll;
+ /* initially create manipulators and set permanent data - stuff you only need to do once */
+ wmManipulatorGroupFnInit setup;
+ /* refresh data, only called if recreate flag is set (WM_manipulatormap_tag_refresh) */
+ wmManipulatorGroupFnRefresh refresh;
+ /* refresh data for drawing, called before each redraw */
+ wmManipulatorGroupFnDrawPrepare draw_prepare;
+
+ /* Keymap init callback for this manipulator-group (optional),
+ * will fall back to default tweak keymap when left NULL. */
+ wmManipulatorGroupFnSetupKeymap setup_keymap;
+
+ /* Optionally subscribe to wmMsgBus events,
+ * these are calculated automatically from RNA properties,
+ * only needed if manipulators depend indirectly on properties. */
+ wmManipulatorGroupFnMsgBusSubscribe message_subscribe;
+
+ /* keymap created with callback from above */
+ struct wmKeyMap *keymap;
+ /* Only for convenient removal. */
+ struct wmKeyConfig *keyconf;
+
+ /* Disable for now, maybe some day we want properties. */
+#if 0
+ /* rna for properties */
+ struct StructRNA *srna;
+#endif
+
+ /* RNA integration */
+ ExtensionRNA ext;
+
+ eWM_ManipulatorGroupTypeFlag flag;
+
+ /* So we know which group type to update. */
+ eWM_ManipulatorMapTypeUpdateFlag type_update_flag;
+
+ /* same as manipulator-maps, so registering/unregistering goes to the correct region */
+ struct wmManipulatorMapType_Params mmap_params;
+
+} wmManipulatorGroupType;
+
+typedef struct wmManipulatorGroup {
+ struct wmManipulatorGroup *next, *prev;
+
+ struct wmManipulatorGroupType *type;
+ ListBase manipulators;
+
+ struct wmManipulatorMap *parent_mmap;
+
+ void *py_instance; /* python stores the class instance here */
+ struct ReportList *reports; /* errors and warnings storage */
+
+ void *customdata;
+ void (*customdata_free)(void *); /* for freeing customdata from above */
+ eWM_ManipulatorGroupInitFlag init_flag;
+} wmManipulatorGroup;
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+/**
+ * Pass a value of this enum to #WM_manipulatormap_draw to tell it what to draw.
+ */
+typedef enum eWM_ManipulatorMapDrawStep {
+ /** Draw 2D manipulator-groups (#WM_MANIPULATORGROUPTYPE_3D not set). */
+ WM_MANIPULATORMAP_DRAWSTEP_2D = 0,
+ /** Draw 3D manipulator-groups (#WM_MANIPULATORGROUPTYPE_3D set). */
+ WM_MANIPULATORMAP_DRAWSTEP_3D,
+} eWM_ManipulatorMapDrawStep;
+#define WM_MANIPULATORMAP_DRAWSTEP_MAX 2
+
+#endif /* __WM_GIZMO_TYPES_H__ */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
new file mode 100644
index 00000000000..bef193f6c56
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c
@@ -0,0 +1,800 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmo/intern/wm_gizmo.c
+ * \ingroup wm
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+#include "BLI_string_utils.h"
+
+#include "BKE_context.h"
+
+#include "GPU_batch.h"
+#include "GPU_glew.h"
+#include "GPU_immediate.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_idprop.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+
+#ifdef WITH_PYTHON
+#include "BPY_extern.h"
+#endif
+
+/* only for own init/exit calls (wm_manipulatortype_init/wm_manipulatortype_free) */
+#include "wm.h"
+
+/* own includes */
+#include "wm_gizmo_wmapi.h"
+#include "wm_gizmo_intern.h"
+
+static void wm_manipulator_register(
+ wmManipulatorGroup *mgroup, wmManipulator *mpr);
+
+/**
+ * \note Follow #wm_operator_create convention.
+ */
+static wmManipulator *wm_manipulator_create(
+ const wmManipulatorType *wt,
+ PointerRNA *properties)
+{
+ BLI_assert(wt != NULL);
+ BLI_assert(wt->struct_size >= sizeof(wmManipulator));
+
+ wmManipulator *mpr = MEM_callocN(
+ wt->struct_size + (sizeof(wmManipulatorProperty) * wt->target_property_defs_len), __func__);
+ mpr->type = wt;
+
+ /* initialize properties, either copy or create */
+ mpr->ptr = MEM_callocN(sizeof(PointerRNA), "wmManipulatorPtrRNA");
+ if (properties && properties->data) {
+ mpr->properties = IDP_CopyProperty(properties->data);
+ }
+ else {
+ IDPropertyTemplate val = {0};
+ mpr->properties = IDP_New(IDP_GROUP, &val, "wmManipulatorProperties");
+ }
+ RNA_pointer_create(G_MAIN->wm.first, wt->srna, mpr->properties, mpr->ptr);
+
+ WM_manipulator_properties_sanitize(mpr->ptr, 0);
+
+ unit_m4(mpr->matrix_space);
+ unit_m4(mpr->matrix_basis);
+ unit_m4(mpr->matrix_offset);
+
+ mpr->drag_part = -1;
+
+ return mpr;
+}
+
+wmManipulator *WM_manipulator_new_ptr(
+ const wmManipulatorType *wt, wmManipulatorGroup *mgroup,
+ PointerRNA *properties)
+{
+ wmManipulator *mpr = wm_manipulator_create(wt, properties);
+
+ wm_manipulator_register(mgroup, mpr);
+
+ if (mpr->type->setup != NULL) {
+ mpr->type->setup(mpr);
+ }
+
+ return mpr;
+}
+
+/**
+ * \param wt: Must be valid,
+ * if you need to check it exists use #WM_manipulator_new_ptr
+ * because callers of this function don't NULL check the return value.
+ */
+wmManipulator *WM_manipulator_new(
+ const char *idname, wmManipulatorGroup *mgroup,
+ PointerRNA *properties)
+{
+ const wmManipulatorType *wt = WM_manipulatortype_find(idname, false);
+ return WM_manipulator_new_ptr(wt, mgroup, properties);
+}
+
+/**
+ * Initialize default values and allocate needed memory for members.
+ */
+static void manipulator_init(wmManipulator *mpr)
+{
+ const float color_default[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+
+ mpr->scale_basis = 1.0f;
+ mpr->line_width = 1.0f;
+
+ /* defaults */
+ copy_v4_v4(mpr->color, color_default);
+ copy_v4_v4(mpr->color_hi, color_default);
+}
+
+/**
+ * Register \a manipulator.
+ *
+ * \param name: name used to create a unique idname for \a manipulator in \a mgroup
+ *
+ * \note Not to be confused with type registration from RNA.
+ */
+static void wm_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr)
+{
+ manipulator_init(mpr);
+ wm_manipulatorgroup_manipulator_register(mgroup, mpr);
+}
+
+/**
+ * \warning this doesn't check #wmManipulatorMap (highlight, selection etc).
+ * Typical use is when freeing the windowing data,
+ * where caller can manage clearing selection, highlight... etc.
+ */
+void WM_manipulator_free(wmManipulator *mpr)
+{
+ if (mpr->type->free != NULL) {
+ mpr->type->free(mpr);
+ }
+
+#ifdef WITH_PYTHON
+ if (mpr->py_instance) {
+ /* do this first in case there are any __del__ functions or
+ * similar that use properties */
+ BPY_DECREF_RNA_INVALIDATE(mpr->py_instance);
+ }
+#endif
+
+ if (mpr->op_data) {
+ for (int i = 0; i < mpr->op_data_len; i++) {
+ WM_operator_properties_free(&mpr->op_data[i].ptr);
+ }
+ MEM_freeN(mpr->op_data);
+ }
+
+ if (mpr->ptr != NULL) {
+ WM_manipulator_properties_free(mpr->ptr);
+ MEM_freeN(mpr->ptr);
+ }
+
+ if (mpr->type->target_property_defs_len != 0) {
+ wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr);
+ for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
+ wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
+ if (mpr_prop->custom_func.free_fn) {
+ mpr_prop->custom_func.free_fn(mpr, mpr_prop);
+ }
+ }
+ }
+
+ MEM_freeN(mpr);
+}
+
+/**
+ * Free \a manipulator and unlink from \a manipulatorlist.
+ * \a manipulatorlist is allowed to be NULL.
+ */
+void WM_manipulator_unlink(ListBase *manipulatorlist, wmManipulatorMap *mmap, wmManipulator *mpr, bContext *C)
+{
+ if (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) {
+ wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
+ }
+ if (mpr->state & WM_MANIPULATOR_STATE_MODAL) {
+ wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false);
+ }
+ /* Unlink instead of setting so we don't run callbacks. */
+ if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
+ WM_manipulator_select_unlink(mmap, mpr);
+ }
+
+ if (manipulatorlist) {
+ BLI_remlink(manipulatorlist, mpr);
+ }
+
+ BLI_assert(mmap->mmap_context.highlight != mpr);
+ BLI_assert(mmap->mmap_context.modal != mpr);
+
+ WM_manipulator_free(mpr);
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Manipulator Creation API
+ *
+ * API for defining data on manipulator creation.
+ *
+ * \{ */
+
+struct wmManipulatorOpElem *WM_manipulator_operator_get(
+ wmManipulator *mpr, int part_index)
+{
+ if (mpr->op_data && ((part_index >= 0) && (part_index < mpr->op_data_len))) {
+ return &mpr->op_data[part_index];
+ }
+ return NULL;
+}
+
+PointerRNA *WM_manipulator_operator_set(
+ wmManipulator *mpr, int part_index,
+ wmOperatorType *ot, IDProperty *properties)
+{
+ BLI_assert(part_index < 255);
+ /* We could pre-allocate these but using multiple is such a rare thing. */
+ if (part_index >= mpr->op_data_len) {
+ mpr->op_data_len = part_index + 1;
+ mpr->op_data = MEM_recallocN(mpr->op_data, sizeof(*mpr->op_data) * mpr->op_data_len);
+ }
+ wmManipulatorOpElem *mpop = &mpr->op_data[part_index];
+ mpop->type = ot;
+
+ if (mpop->ptr.data) {
+ WM_operator_properties_free(&mpop->ptr);
+ }
+ WM_operator_properties_create_ptr(&mpop->ptr, ot);
+
+ if (properties) {
+ mpop->ptr.data = properties;
+ }
+
+ return &mpop->ptr;
+}
+
+static void wm_manipulator_set_matrix_rotation_from_z_axis__internal(
+ float matrix[4][4], const float z_axis[3])
+{
+ /* old code, seems we can use simpler method */
+#if 0
+ const float z_global[3] = {0.0f, 0.0f, 1.0f};
+ float rot[3][3];
+
+ rotation_between_vecs_to_mat3(rot, z_global, z_axis);
+ copy_v3_v3(matrix[0], rot[0]);
+ copy_v3_v3(matrix[1], rot[1]);
+ copy_v3_v3(matrix[2], rot[2]);
+#else
+ normalize_v3_v3(matrix[2], z_axis);
+ ortho_basis_v3v3_v3(matrix[0], matrix[1], matrix[2]);
+#endif
+
+}
+
+static void wm_manipulator_set_matrix_rotation_from_yz_axis__internal(
+ float matrix[4][4], const float y_axis[3], const float z_axis[3])
+{
+ normalize_v3_v3(matrix[1], y_axis);
+ normalize_v3_v3(matrix[2], z_axis);
+ cross_v3_v3v3(matrix[0], matrix[1], matrix[2]);
+ normalize_v3(matrix[0]);
+}
+
+/**
+ * wmManipulator.matrix utils.
+ */
+void WM_manipulator_set_matrix_rotation_from_z_axis(
+ wmManipulator *mpr, const float z_axis[3])
+{
+ wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_basis, z_axis);
+}
+void WM_manipulator_set_matrix_rotation_from_yz_axis(
+ wmManipulator *mpr, const float y_axis[3], const float z_axis[3])
+{
+ wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_basis, y_axis, z_axis);
+}
+void WM_manipulator_set_matrix_location(wmManipulator *mpr, const float origin[3])
+{
+ copy_v3_v3(mpr->matrix_basis[3], origin);
+}
+
+/**
+ * wmManipulator.matrix_offset utils.
+ */
+void WM_manipulator_set_matrix_offset_rotation_from_z_axis(
+ wmManipulator *mpr, const float z_axis[3])
+{
+ wm_manipulator_set_matrix_rotation_from_z_axis__internal(mpr->matrix_offset, z_axis);
+}
+void WM_manipulator_set_matrix_offset_rotation_from_yz_axis(
+ wmManipulator *mpr, const float y_axis[3], const float z_axis[3])
+{
+ wm_manipulator_set_matrix_rotation_from_yz_axis__internal(mpr->matrix_offset, y_axis, z_axis);
+}
+void WM_manipulator_set_matrix_offset_location(wmManipulator *mpr, const float offset[3])
+{
+ copy_v3_v3(mpr->matrix_offset[3], offset);
+}
+
+void WM_manipulator_set_flag(wmManipulator *mpr, const int flag, const bool enable)
+{
+ if (enable) {
+ mpr->flag |= flag;
+ }
+ else {
+ mpr->flag &= ~flag;
+ }
+}
+
+void WM_manipulator_set_scale(wmManipulator *mpr, const float scale)
+{
+ mpr->scale_basis = scale;
+}
+
+void WM_manipulator_set_line_width(wmManipulator *mpr, const float line_width)
+{
+ mpr->line_width = line_width;
+}
+
+/**
+ * Set manipulator rgba colors.
+ *
+ * \param col Normal state color.
+ * \param col_hi Highlighted state color.
+ */
+void WM_manipulator_get_color(const wmManipulator *mpr, float color[4])
+{
+ copy_v4_v4(color, mpr->color);
+}
+void WM_manipulator_set_color(wmManipulator *mpr, const float color[4])
+{
+ copy_v4_v4(mpr->color, color);
+}
+
+void WM_manipulator_get_color_highlight(const wmManipulator *mpr, float color_hi[4])
+{
+ copy_v4_v4(color_hi, mpr->color_hi);
+}
+void WM_manipulator_set_color_highlight(wmManipulator *mpr, const float color_hi[4])
+{
+ copy_v4_v4(mpr->color_hi, color_hi);
+}
+
+
+/** \} */ // Manipulator Creation API
+
+
+/* -------------------------------------------------------------------- */
+/** \name Manipulator Callback Assignment
+ *
+ * \{ */
+
+void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn)
+{
+ mpr->custom_modal = fn;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/**
+ * Add/Remove \a manipulator to selection.
+ * Reallocates memory for selected manipulators so better not call for selecting multiple ones.
+ *
+ * \return if the selection has changed.
+ */
+bool wm_manipulator_select_set_ex(
+ wmManipulatorMap *mmap, wmManipulator *mpr, bool select,
+ bool use_array, bool use_callback)
+{
+ bool changed = false;
+
+ if (select) {
+ if ((mpr->state & WM_MANIPULATOR_STATE_SELECT) == 0) {
+ if (use_array) {
+ wm_manipulatormap_select_array_push_back(mmap, mpr);
+ }
+ mpr->state |= WM_MANIPULATOR_STATE_SELECT;
+ changed = true;
+ }
+ }
+ else {
+ if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
+ if (use_array) {
+ wm_manipulatormap_select_array_remove(mmap, mpr);
+ }
+ mpr->state &= ~WM_MANIPULATOR_STATE_SELECT;
+ changed = true;
+ }
+ }
+
+ /* In the case of unlinking we only want to remove from the array
+ * and not write to the external state */
+ if (use_callback && changed) {
+ if (mpr->type->select_refresh) {
+ mpr->type->select_refresh(mpr);
+ }
+ }
+
+ return changed;
+}
+
+/* Remove from selection array without running callbacks. */
+bool WM_manipulator_select_unlink(wmManipulatorMap *mmap, wmManipulator *mpr)
+{
+ return wm_manipulator_select_set_ex(mmap, mpr, false, true, false);
+}
+
+bool WM_manipulator_select_set(wmManipulatorMap *mmap, wmManipulator *mpr, bool select)
+{
+ return wm_manipulator_select_set_ex(mmap, mpr, select, true, true);
+}
+
+void WM_manipulator_highlight_set(wmManipulatorMap *mmap, wmManipulator *mpr)
+{
+ wm_manipulatormap_highlight_set(mmap, NULL, mpr, mpr ? mpr->highlight_part : 0);
+}
+
+bool wm_manipulator_select_and_highlight(bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr)
+{
+ if (WM_manipulator_select_set(mmap, mpr, true)) {
+ wm_manipulatormap_highlight_set(mmap, C, mpr, mpr->highlight_part);
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+/**
+ * Special function to run from setup so manipulators start out interactive.
+ *
+ * We could do this when linking them, but this complicates things since the window update code needs to run first.
+ */
+void WM_manipulator_modal_set_from_setup(
+ struct wmManipulatorMap *mmap, struct bContext *C,
+ struct wmManipulator *mpr, int part_index, const wmEvent *event)
+{
+ mpr->highlight_part = part_index;
+ WM_manipulator_highlight_set(mmap, mpr);
+ if (false) {
+ wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
+ }
+ else {
+ /* WEAK: but it works. */
+ WM_operator_name_call(C, "MANIPULATORGROUP_OT_manipulator_tweak", WM_OP_INVOKE_DEFAULT, NULL);
+ }
+}
+
+void wm_manipulator_calculate_scale(wmManipulator *mpr, const bContext *C)
+{
+ const RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ float scale = UI_DPI_FAC;
+
+ if ((mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCALE) == 0) {
+ scale *= U.manipulator_size;
+ if (rv3d) {
+ /* 'ED_view3d_pixel_size' includes 'U.pixelsize', remove it. */
+ float matrix_world[4][4];
+ if (mpr->type->matrix_basis_get) {
+ float matrix_basis[4][4];
+ mpr->type->matrix_basis_get(mpr, matrix_basis);
+ mul_m4_m4m4(matrix_world, mpr->matrix_space, matrix_basis);
+ }
+ else {
+ mul_m4_m4m4(matrix_world, mpr->matrix_space, mpr->matrix_basis);
+ }
+
+ /* Exclude matrix_offset from scale. */
+ scale *= ED_view3d_pixel_size_no_ui_scale(rv3d, matrix_world[3]);
+ }
+ else {
+ scale *= 0.02f;
+ }
+ }
+
+ mpr->scale_final = mpr->scale_basis * scale;
+}
+
+static void manipulator_update_prop_data(wmManipulator *mpr)
+{
+ /* manipulator property might have been changed, so update manipulator */
+ if (mpr->type->property_update) {
+ wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr);
+ for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
+ wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
+ if (WM_manipulator_target_property_is_valid(mpr_prop)) {
+ mpr->type->property_update(mpr, mpr_prop);
+ }
+ }
+ }
+}
+
+void wm_manipulator_update(wmManipulator *mpr, const bContext *C, const bool refresh_map)
+{
+ if (refresh_map) {
+ manipulator_update_prop_data(mpr);
+ }
+ wm_manipulator_calculate_scale(mpr, C);
+}
+
+int wm_manipulator_is_visible(wmManipulator *mpr)
+{
+ if (mpr->flag & WM_MANIPULATOR_HIDDEN) {
+ return 0;
+ }
+ if ((mpr->state & WM_MANIPULATOR_STATE_MODAL) &&
+ !(mpr->flag & (WM_MANIPULATOR_DRAW_MODAL | WM_MANIPULATOR_DRAW_VALUE)))
+ {
+ /* don't draw while modal (dragging) */
+ return 0;
+ }
+ if ((mpr->flag & WM_MANIPULATOR_DRAW_HOVER) &&
+ !(mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) &&
+ !(mpr->state & WM_MANIPULATOR_STATE_SELECT)) /* still draw selected manipulators */
+ {
+ /* update but don't draw */
+ return WM_MANIPULATOR_IS_VISIBLE_UPDATE;
+ }
+
+ return WM_MANIPULATOR_IS_VISIBLE_UPDATE | WM_MANIPULATOR_IS_VISIBLE_DRAW;
+}
+
+void WM_manipulator_calc_matrix_final_params(
+ const wmManipulator *mpr,
+ const struct WM_ManipulatorMatrixParams *params,
+ float r_mat[4][4])
+{
+ const float (* const matrix_space)[4] = params->matrix_space ? params->matrix_space : mpr->matrix_space;
+ const float (* const matrix_basis)[4] = params->matrix_basis ? params->matrix_basis : mpr->matrix_basis;
+ const float (* const matrix_offset)[4] = params->matrix_offset ? params->matrix_offset : mpr->matrix_offset;
+ const float *scale_final = params->scale_final ? params->scale_final : &mpr->scale_final;
+
+ float final_matrix[4][4];
+ if (params->matrix_basis == NULL && mpr->type->matrix_basis_get) {
+ mpr->type->matrix_basis_get(mpr, final_matrix);
+ }
+ else {
+ copy_m4_m4(final_matrix, matrix_basis);
+ }
+
+ if (mpr->flag & WM_MANIPULATOR_DRAW_NO_SCALE) {
+ mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
+ }
+ else {
+ if (mpr->flag & WM_MANIPULATOR_DRAW_OFFSET_SCALE) {
+ mul_mat3_m4_fl(final_matrix, *scale_final);
+ mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
+ }
+ else {
+ mul_m4_m4m4(final_matrix, final_matrix, matrix_offset);
+ mul_mat3_m4_fl(final_matrix, *scale_final);
+ }
+ }
+
+ mul_m4_m4m4(r_mat, matrix_space, final_matrix);
+}
+
+void WM_manipulator_calc_matrix_final_no_offset(const wmManipulator *mpr, float r_mat[4][4])
+{
+ float mat_identity[4][4];
+ unit_m4(mat_identity);
+
+ WM_manipulator_calc_matrix_final_params(
+ mpr,
+ &((struct WM_ManipulatorMatrixParams) {
+ .matrix_space = NULL,
+ .matrix_basis = NULL,
+ .matrix_offset = mat_identity,
+ .scale_final = NULL,
+ }), r_mat
+ );
+}
+
+void WM_manipulator_calc_matrix_final(const wmManipulator *mpr, float r_mat[4][4])
+{
+ WM_manipulator_calc_matrix_final_params(
+ mpr,
+ &((struct WM_ManipulatorMatrixParams) {
+ .matrix_space = NULL,
+ .matrix_basis = NULL,
+ .matrix_offset = NULL,
+ .scale_final = NULL,
+ }), r_mat
+ );
+}
+
+/** \name Manipulator Propery Access
+ *
+ * Matches `WM_operator_properties` conventions.
+ *
+ * \{ */
+
+
+void WM_manipulator_properties_create_ptr(PointerRNA *ptr, wmManipulatorType *wt)
+{
+ RNA_pointer_create(NULL, wt->srna, NULL, ptr);
+}
+
+void WM_manipulator_properties_create(PointerRNA *ptr, const char *wtstring)
+{
+ const wmManipulatorType *wt = WM_manipulatortype_find(wtstring, false);
+
+ if (wt)
+ WM_manipulator_properties_create_ptr(ptr, (wmManipulatorType *)wt);
+ else
+ RNA_pointer_create(NULL, &RNA_ManipulatorProperties, NULL, ptr);
+}
+
+/* similar to the function above except its uses ID properties
+ * used for keymaps and macros */
+void WM_manipulator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *wtstring)
+{
+ if (*properties == NULL) {
+ IDPropertyTemplate val = {0};
+ *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
+ }
+
+ if (*ptr == NULL) {
+ *ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
+ WM_manipulator_properties_create(*ptr, wtstring);
+ }
+
+ (*ptr)->data = *properties;
+
+}
+
+void WM_manipulator_properties_sanitize(PointerRNA *ptr, const bool no_context)
+{
+ RNA_STRUCT_BEGIN (ptr, prop)
+ {
+ switch (RNA_property_type(prop)) {
+ case PROP_ENUM:
+ if (no_context)
+ RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
+ else
+ RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
+ break;
+ case PROP_POINTER:
+ {
+ StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
+
+ /* recurse into manipulator properties */
+ if (RNA_struct_is_a(ptype, &RNA_ManipulatorProperties)) {
+ PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
+ WM_manipulator_properties_sanitize(&opptr, no_context);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ RNA_STRUCT_END;
+}
+
+
+/** set all props to their default,
+ * \param do_update Only update un-initialized props.
+ *
+ * \note, theres nothing specific to manipulators here.
+ * this could be made a general function.
+ */
+bool WM_manipulator_properties_default(PointerRNA *ptr, const bool do_update)
+{
+ bool changed = false;
+ RNA_STRUCT_BEGIN (ptr, prop)
+ {
+ switch (RNA_property_type(prop)) {
+ case PROP_POINTER:
+ {
+ StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
+ if (ptype != &RNA_Struct) {
+ PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
+ changed |= WM_manipulator_properties_default(&opptr, do_update);
+ }
+ break;
+ }
+ default:
+ if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) {
+ if (RNA_property_reset(ptr, prop, -1)) {
+ changed = true;
+ }
+ }
+ break;
+ }
+ }
+ RNA_STRUCT_END;
+
+ return changed;
+}
+
+/* remove all props without PROP_SKIP_SAVE */
+void WM_manipulator_properties_reset(wmManipulator *mpr)
+{
+ if (mpr->ptr->data) {
+ PropertyRNA *iterprop;
+ iterprop = RNA_struct_iterator_property(mpr->type->srna);
+
+ RNA_PROP_BEGIN (mpr->ptr, itemptr, iterprop)
+ {
+ PropertyRNA *prop = itemptr.data;
+
+ if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
+ const char *identifier = RNA_property_identifier(prop);
+ RNA_struct_idprops_unset(mpr->ptr, identifier);
+ }
+ }
+ RNA_PROP_END;
+ }
+}
+
+void WM_manipulator_properties_clear(PointerRNA *ptr)
+{
+ IDProperty *properties = ptr->data;
+
+ if (properties) {
+ IDP_ClearProperty(properties);
+ }
+}
+
+void WM_manipulator_properties_free(PointerRNA *ptr)
+{
+ IDProperty *properties = ptr->data;
+
+ if (properties) {
+ IDP_FreeProperty(properties);
+ MEM_freeN(properties);
+ ptr->data = NULL; /* just in case */
+ }
+}
+
+/** \} */
+
+/** \name General Utilities
+ *
+ * \{ */
+
+bool WM_manipulator_context_check_drawstep(const struct bContext *C, eWM_ManipulatorMapDrawStep step)
+{
+ switch (step) {
+ case WM_MANIPULATORMAP_DRAWSTEP_2D:
+ {
+ break;
+ }
+ case WM_MANIPULATORMAP_DRAWSTEP_3D:
+ {
+ wmWindowManager *wm = CTX_wm_manager(C);
+ if (ED_screen_animation_playing(wm)) {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
new file mode 100644
index 00000000000..83af1a71163
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
@@ -0,0 +1,949 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmo/intern/wm_gizmo_group.c
+ * \ingroup wm
+ *
+ * \name Manipulator-Group
+ *
+ * Manipulator-groups store and manage groups of manipulators. They can be
+ * attached to modal handlers and have own keymaps.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_workspace.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "wm_event_system.h"
+
+#include "ED_screen.h"
+#include "ED_undo.h"
+
+/* own includes */
+#include "wm_gizmo_wmapi.h"
+#include "wm_gizmo_intern.h"
+
+#ifdef WITH_PYTHON
+# include "BPY_extern.h"
+#endif
+
+/* Allow manipulator part's to be single click only,
+ * dragging falls back to activating their 'drag_part' action. */
+#define USE_DRAG_DETECT
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorGroup
+ *
+ * \{ */
+
+/**
+ * Create a new manipulator-group from \a wgt.
+ */
+wmManipulatorGroup *wm_manipulatorgroup_new_from_type(
+ wmManipulatorMap *mmap, wmManipulatorGroupType *wgt)
+{
+ wmManipulatorGroup *mgroup = MEM_callocN(sizeof(*mgroup), "manipulator-group");
+ mgroup->type = wgt;
+
+ /* keep back-link */
+ mgroup->parent_mmap = mmap;
+
+ BLI_addtail(&mmap->groups, mgroup);
+
+ return mgroup;
+}
+
+void wm_manipulatorgroup_free(bContext *C, wmManipulatorGroup *mgroup)
+{
+ wmManipulatorMap *mmap = mgroup->parent_mmap;
+
+ /* Similar to WM_manipulator_unlink, but only to keep mmap state correct,
+ * we don't want to run callbacks. */
+ if (mmap->mmap_context.highlight && mmap->mmap_context.highlight->parent_mgroup == mgroup) {
+ wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
+ }
+ if (mmap->mmap_context.modal && mmap->mmap_context.modal->parent_mgroup == mgroup) {
+ wm_manipulatormap_modal_set(mmap, C, mmap->mmap_context.modal, NULL, false);
+ }
+
+ for (wmManipulator *mpr = mgroup->manipulators.first, *mpr_next; mpr; mpr = mpr_next) {
+ mpr_next = mpr->next;
+ if (mmap->mmap_context.select.len) {
+ WM_manipulator_select_unlink(mmap, mpr);
+ }
+ WM_manipulator_free(mpr);
+ }
+ BLI_listbase_clear(&mgroup->manipulators);
+
+#ifdef WITH_PYTHON
+ if (mgroup->py_instance) {
+ /* do this first in case there are any __del__ functions or
+ * similar that use properties */
+ BPY_DECREF_RNA_INVALIDATE(mgroup->py_instance);
+ }
+#endif
+
+ if (mgroup->reports && (mgroup->reports->flag & RPT_FREE)) {
+ BKE_reports_clear(mgroup->reports);
+ MEM_freeN(mgroup->reports);
+ }
+
+ if (mgroup->customdata_free) {
+ mgroup->customdata_free(mgroup->customdata);
+ }
+ else {
+ MEM_SAFE_FREE(mgroup->customdata);
+ }
+
+ BLI_remlink(&mmap->groups, mgroup);
+
+ MEM_freeN(mgroup);
+}
+
+/**
+ * Add \a manipulator to \a mgroup and make sure its name is unique within the group.
+ */
+void wm_manipulatorgroup_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr)
+{
+ BLI_assert(BLI_findindex(&mgroup->manipulators, mpr) == -1);
+ BLI_addtail(&mgroup->manipulators, mpr);
+ mpr->parent_mgroup = mgroup;
+}
+
+wmManipulator *wm_manipulatorgroup_find_intersected_manipulator(
+ const wmManipulatorGroup *mgroup, bContext *C, const wmEvent *event,
+ int *r_part)
+{
+ for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ if (mpr->type->test_select && (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) {
+ if ((*r_part = mpr->type->test_select(C, mpr, event)) != -1) {
+ return mpr;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Adds all manipulators of \a mgroup that can be selected to the head of \a listbase. Added items need freeing!
+ */
+void wm_manipulatorgroup_intersectable_manipulators_to_list(const wmManipulatorGroup *mgroup, ListBase *listbase)
+{
+ for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ if ((mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) {
+ if (((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) && mpr->type->draw_select) ||
+ ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0 && mpr->type->test_select))
+ {
+ BLI_addhead(listbase, BLI_genericNodeN(mpr));
+ }
+ }
+ }
+}
+
+void wm_manipulatorgroup_ensure_initialized(wmManipulatorGroup *mgroup, const bContext *C)
+{
+ /* prepare for first draw */
+ if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0)) {
+ mgroup->type->setup(C, mgroup);
+
+ /* Not ideal, initialize keymap here, needed for RNA runtime generated manipulators. */
+ wmManipulatorGroupType *wgt = mgroup->type;
+ if (wgt->keymap == NULL) {
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wm_manipulatorgrouptype_setup_keymap(wgt, wm->defaultconf);
+ BLI_assert(wgt->keymap != NULL);
+ }
+ mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_SETUP;
+ }
+
+ /* refresh may be called multiple times, this just ensures its called at least once before we draw. */
+ if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_REFRESH) == 0)) {
+ if (mgroup->type->refresh) {
+ mgroup->type->refresh(C, mgroup);
+ }
+ mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_REFRESH;
+ }
+}
+
+bool WM_manipulator_group_type_poll(const bContext *C, const struct wmManipulatorGroupType *wgt)
+{
+ /* If we're tagged, only use compatible. */
+ if (wgt->owner_id[0] != '\0') {
+ const WorkSpace *workspace = CTX_wm_workspace(C);
+ if (BKE_workspace_owner_id_check(workspace, wgt->owner_id) == false) {
+ return false;
+ }
+ }
+ /* Check for poll function, if manipulator-group belongs to an operator, also check if the operator is running. */
+ return (!wgt->poll || wgt->poll(C, (wmManipulatorGroupType *)wgt));
+}
+
+bool wm_manipulatorgroup_is_visible_in_drawstep(
+ const wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep)
+{
+ switch (drawstep) {
+ case WM_MANIPULATORMAP_DRAWSTEP_2D:
+ return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0;
+ case WM_MANIPULATORMAP_DRAWSTEP_3D:
+ return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D);
+ default:
+ BLI_assert(0);
+ return false;
+ }
+}
+
+bool wm_manipulatorgroup_is_any_selected(const wmManipulatorGroup *mgroup)
+{
+ if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT) {
+ for (const wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/** \} */
+
+/** \name Manipulator operators
+ *
+ * Basic operators for manipulator interaction with user configurable keymaps.
+ *
+ * \{ */
+
+static int manipulator_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ ARegion *ar = CTX_wm_region(C);
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ wmManipulator *highlight = mmap->mmap_context.highlight;
+
+ bool extend = RNA_boolean_get(op->ptr, "extend");
+ bool deselect = RNA_boolean_get(op->ptr, "deselect");
+ bool toggle = RNA_boolean_get(op->ptr, "toggle");
+
+ /* deselect all first */
+ if (extend == false && deselect == false && toggle == false) {
+ wm_manipulatormap_deselect_all(mmap);
+ BLI_assert(msel->items == NULL && msel->len == 0);
+ UNUSED_VARS_NDEBUG(msel);
+ }
+
+ if (highlight) {
+ const bool is_selected = (highlight->state & WM_MANIPULATOR_STATE_SELECT);
+ bool redraw = false;
+
+ if (toggle) {
+ /* toggle: deselect if already selected, else select */
+ deselect = is_selected;
+ }
+
+ if (deselect) {
+ if (is_selected && WM_manipulator_select_set(mmap, highlight, false)) {
+ redraw = true;
+ }
+ }
+ else if (wm_manipulator_select_and_highlight(C, mmap, highlight)) {
+ redraw = true;
+ }
+
+ if (redraw) {
+ ED_region_tag_redraw(ar);
+ }
+
+ return OPERATOR_FINISHED;
+ }
+ else {
+ BLI_assert(0);
+ return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
+ }
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+void MANIPULATORGROUP_OT_manipulator_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Gizmo Select";
+ ot->description = "Select the currently highlighted manipulator";
+ ot->idname = "MANIPULATORGROUP_OT_manipulator_select";
+
+ /* api callbacks */
+ ot->invoke = manipulator_select_invoke;
+
+ ot->flag = OPTYPE_UNDO;
+
+ WM_operator_properties_mouse_select(ot);
+}
+
+typedef struct ManipulatorTweakData {
+ wmManipulatorMap *mmap;
+ wmManipulatorGroup *mgroup;
+ wmManipulator *mpr_modal;
+
+ int init_event; /* initial event type */
+ int flag; /* tweak flags */
+
+#ifdef USE_DRAG_DETECT
+ /* True until the mouse is moved (only use when the operator has no modal).
+ * this allows some manipulators to be click-only. */
+ enum {
+ /* Don't detect dragging. */
+ DRAG_NOP = 0,
+ /* Detect dragging (wait until a drag or click is detected). */
+ DRAG_DETECT,
+ /* Drag has started, idle until there is no active modal operator.
+ * This is needed because finishing the modal operator also exits
+ * the modal manipulator state (un-grabbs the cursor).
+ * Ideally this workaround could be removed later. */
+ DRAG_IDLE,
+ } drag_state;
+#endif
+
+} ManipulatorTweakData;
+
+static bool manipulator_tweak_start(
+ bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr, const wmEvent *event)
+{
+ /* activate highlighted manipulator */
+ wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
+
+ return (mpr->state & WM_MANIPULATOR_STATE_MODAL);
+}
+
+static bool manipulator_tweak_start_and_finish(
+ bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr, const wmEvent *event, bool *r_is_modal)
+{
+ wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
+ if (r_is_modal) {
+ *r_is_modal = false;
+ }
+ if (mpop && mpop->type) {
+
+ /* Undo/Redo */
+ if (mpop->is_redo) {
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmOperator *op = WM_operator_last_redo(C);
+
+ /* We may want to enable this, for now the manipulator can manage it's own properties. */
+#if 0
+ IDP_MergeGroup(mpop->ptr.data, op->properties, false);
+#endif
+
+ WM_operator_free_all_after(wm, op);
+ ED_undo_pop_op(C, op);
+ }
+
+ /* XXX temporary workaround for modal manipulator operator
+ * conflicting with modal operator attached to manipulator */
+ if (mpop->type->modal) {
+ /* activate highlighted manipulator */
+ wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
+ if (r_is_modal) {
+ *r_is_modal = true;
+ }
+ }
+ else {
+ /* Allow for 'button' manipulators, single click to run an action. */
+ WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr);
+ }
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel, bool clear_modal)
+{
+ ManipulatorTweakData *mtweak = op->customdata;
+ if (mtweak->mpr_modal->type->exit) {
+ mtweak->mpr_modal->type->exit(C, mtweak->mpr_modal, cancel);
+ }
+ if (clear_modal) {
+ /* The manipulator may have been removed. */
+ if ((BLI_findindex(&mtweak->mmap->groups, mtweak->mgroup) != -1) &&
+ (BLI_findindex(&mtweak->mgroup->manipulators, mtweak->mpr_modal) != -1))
+ {
+ wm_manipulatormap_modal_set(mtweak->mmap, C, mtweak->mpr_modal, NULL, false);
+ }
+ }
+ MEM_freeN(mtweak);
+}
+
+static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ManipulatorTweakData *mtweak = op->customdata;
+ wmManipulator *mpr = mtweak->mpr_modal;
+ int retval = OPERATOR_PASS_THROUGH;
+ bool clear_modal = true;
+
+ if (mpr == NULL) {
+ BLI_assert(0);
+ return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
+ }
+
+#ifdef USE_DRAG_DETECT
+ wmManipulatorMap *mmap = mtweak->mmap;
+ if (mtweak->drag_state == DRAG_DETECT) {
+ if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (len_manhattan_v2v2_int(&event->x, mmap->mmap_context.event_xy) > 2) {
+ mtweak->drag_state = DRAG_IDLE;
+ mpr->highlight_part = mpr->drag_part;
+ }
+ }
+ else if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
+ mtweak->drag_state = DRAG_NOP;
+ retval = OPERATOR_FINISHED;
+ }
+
+ if (mtweak->drag_state != DRAG_DETECT) {
+ /* Follow logic in 'manipulator_tweak_invoke' */
+ bool is_modal = false;
+ if (manipulator_tweak_start_and_finish(C, mmap, mpr, event, &is_modal)) {
+ if (is_modal) {
+ clear_modal = false;
+ }
+ }
+ else {
+ if (!manipulator_tweak_start(C, mmap, mpr, event)) {
+ retval = OPERATOR_FINISHED;
+ }
+ }
+ }
+ }
+ if (mtweak->drag_state == DRAG_IDLE) {
+ if (mmap->mmap_context.modal != NULL) {
+ return OPERATOR_PASS_THROUGH;
+ }
+ else {
+ manipulator_tweak_finish(C, op, false, false);
+ return OPERATOR_FINISHED;
+ }
+ }
+#endif /* USE_DRAG_DETECT */
+
+ if (retval == OPERATOR_FINISHED) {
+ /* pass */
+ }
+ else if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
+ retval = OPERATOR_FINISHED;
+ }
+ else if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case TWEAK_MODAL_CANCEL:
+ retval = OPERATOR_CANCELLED;
+ break;
+ case TWEAK_MODAL_CONFIRM:
+ retval = OPERATOR_FINISHED;
+ break;
+ case TWEAK_MODAL_PRECISION_ON:
+ mtweak->flag |= WM_MANIPULATOR_TWEAK_PRECISE;
+ break;
+ case TWEAK_MODAL_PRECISION_OFF:
+ mtweak->flag &= ~WM_MANIPULATOR_TWEAK_PRECISE;
+ break;
+
+ case TWEAK_MODAL_SNAP_ON:
+ mtweak->flag |= WM_MANIPULATOR_TWEAK_SNAP;
+ break;
+ case TWEAK_MODAL_SNAP_OFF:
+ mtweak->flag &= ~WM_MANIPULATOR_TWEAK_SNAP;
+ break;
+ }
+ }
+
+ if (retval != OPERATOR_PASS_THROUGH) {
+ manipulator_tweak_finish(C, op, retval != OPERATOR_FINISHED, clear_modal);
+ return retval;
+ }
+
+ /* handle manipulator */
+ wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal;
+ if (modal_fn) {
+ int modal_retval = modal_fn(C, mpr, event, mtweak->flag);
+
+ if ((modal_retval & OPERATOR_RUNNING_MODAL) == 0) {
+ manipulator_tweak_finish(C, op, (modal_retval & OPERATOR_CANCELLED) != 0, true);
+ return OPERATOR_FINISHED;
+ }
+
+ /* Ugly hack to send manipulator events */
+ ((wmEvent *)event)->type = EVT_MANIPULATOR_UPDATE;
+ }
+
+ /* always return PASS_THROUGH so modal handlers
+ * with manipulators attached can update */
+ BLI_assert(retval == OPERATOR_PASS_THROUGH);
+ return OPERATOR_PASS_THROUGH;
+}
+
+static int manipulator_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion *ar = CTX_wm_region(C);
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ wmManipulator *mpr = mmap->mmap_context.highlight;
+
+ /* Needed for single click actions which don't enter modal state. */
+ WM_tooltip_clear(C, CTX_wm_window(C));
+
+ if (!mpr) {
+ /* wm_handlers_do_intern shouldn't let this happen */
+ BLI_assert(0);
+ return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
+ }
+
+ bool use_drag_fallback = false;
+
+#ifdef USE_DRAG_DETECT
+ use_drag_fallback = !ELEM(mpr->drag_part, -1, mpr->highlight_part);
+#endif
+
+ if (use_drag_fallback == false) {
+ if (manipulator_tweak_start_and_finish(C, mmap, mpr, event, NULL)) {
+ return OPERATOR_FINISHED;
+ }
+ }
+
+ bool use_drag_detect = false;
+#ifdef USE_DRAG_DETECT
+ if (use_drag_fallback) {
+ wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
+ if (mpop && mpop->type) {
+ if (mpop->type->modal == NULL) {
+ use_drag_detect = true;
+ }
+ }
+ }
+#endif
+
+ if (use_drag_detect == false) {
+ if (!manipulator_tweak_start(C, mmap, mpr, event)) {
+ /* failed to start */
+ return OPERATOR_PASS_THROUGH;
+ }
+ }
+
+ ManipulatorTweakData *mtweak = MEM_mallocN(sizeof(ManipulatorTweakData), __func__);
+
+ mtweak->init_event = WM_userdef_event_type_from_keymap_type(event->type);
+ mtweak->mpr_modal = mmap->mmap_context.highlight;
+ mtweak->mgroup = mtweak->mpr_modal->parent_mgroup;
+ mtweak->mmap = mmap;
+ mtweak->flag = 0;
+
+#ifdef USE_DRAG_DETECT
+ mtweak->drag_state = use_drag_detect ? DRAG_DETECT : DRAG_NOP;
+#endif
+
+ op->customdata = mtweak;
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void MANIPULATORGROUP_OT_manipulator_tweak(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Gizmo Tweak";
+ ot->description = "Tweak the active gizmo";
+ ot->idname = "MANIPULATORGROUP_OT_manipulator_tweak";
+
+ /* api callbacks */
+ ot->invoke = manipulator_tweak_invoke;
+ ot->modal = manipulator_tweak_modal;
+
+ /* TODO(campbell) This causes problems tweaking settings for operators,
+ * need to find a way to support this. */
+#if 0
+ ot->flag = OPTYPE_UNDO;
+#endif
+}
+
+/** \} */
+
+
+static wmKeyMap *manipulatorgroup_tweak_modal_keymap(wmKeyConfig *keyconf, const char *mgroupname)
+{
+ wmKeyMap *keymap;
+ char name[KMAP_MAX_NAME];
+
+ static EnumPropertyItem modal_items[] = {
+ {TWEAK_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
+ {TWEAK_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
+ {TWEAK_MODAL_PRECISION_ON, "PRECISION_ON", 0, "Enable Precision", ""},
+ {TWEAK_MODAL_PRECISION_OFF, "PRECISION_OFF", 0, "Disable Precision", ""},
+ {TWEAK_MODAL_SNAP_ON, "SNAP_ON", 0, "Enable Snap", ""},
+ {TWEAK_MODAL_SNAP_OFF, "SNAP_OFF", 0, "Disable Snap", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+
+ BLI_snprintf(name, sizeof(name), "%s Tweak Modal Map", mgroupname);
+ keymap = WM_modalkeymap_get(keyconf, name);
+
+ /* this function is called for each spacetype, only needs to add map once */
+ if (keymap && keymap->modal_items)
+ return NULL;
+
+ keymap = WM_modalkeymap_add(keyconf, name, modal_items);
+
+
+ /* items for modal map */
+ WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
+ WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
+
+ WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
+ WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
+
+ WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
+ WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
+ WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
+ WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
+
+ WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
+ WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
+ WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
+
+ WM_modalkeymap_assign(keymap, "MANIPULATORGROUP_OT_manipulator_tweak");
+
+ return keymap;
+}
+
+/**
+ * Common default keymap for manipulator groups
+ */
+wmKeyMap *WM_manipulatorgroup_keymap_common(
+ const wmManipulatorGroupType *wgt, wmKeyConfig *config)
+{
+ /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */
+ wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid);
+
+ WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", LEFTMOUSE, KM_PRESS, KM_ANY, 0);
+ manipulatorgroup_tweak_modal_keymap(config, wgt->name);
+
+ return km;
+}
+
+/**
+ * Variation of #WM_manipulatorgroup_keymap_common but with keymap items for selection
+ */
+wmKeyMap *WM_manipulatorgroup_keymap_common_select(
+ const wmManipulatorGroupType *wgt, wmKeyConfig *config)
+{
+ /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */
+ wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid);
+
+ WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0);
+ WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", EVT_TWEAK_S, KM_ANY, 0, 0);
+ manipulatorgroup_tweak_modal_keymap(config, wgt->name);
+
+ wmKeyMapItem *kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, 0, 0);
+ RNA_boolean_set(kmi->ptr, "extend", false);
+ RNA_boolean_set(kmi->ptr, "deselect", false);
+ RNA_boolean_set(kmi->ptr, "toggle", false);
+ kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "extend", false);
+ RNA_boolean_set(kmi->ptr, "deselect", false);
+ RNA_boolean_set(kmi->ptr, "toggle", true);
+
+ return km;
+}
+
+/** \} */ /* wmManipulatorGroup */
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorGroupType
+ *
+ * \{ */
+
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr(
+ struct wmManipulatorMapType *mmap_type,
+ const wmManipulatorGroupType *wgt)
+{
+ /* could use hash lookups as operator types do, for now simple search. */
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
+ wgt_ref;
+ wgt_ref = wgt_ref->next)
+ {
+ if (wgt_ref->type == wgt) {
+ return wgt_ref;
+ }
+ }
+ return NULL;
+}
+
+struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find(
+ struct wmManipulatorMapType *mmap_type,
+ const char *idname)
+{
+ /* could use hash lookups as operator types do, for now simple search. */
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
+ wgt_ref;
+ wgt_ref = wgt_ref->next)
+ {
+ if (STREQ(idname, wgt_ref->type->idname)) {
+ return wgt_ref;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Use this for registering manipulators on startup. For runtime, use #WM_manipulatormaptype_group_link_runtime.
+ */
+wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link(
+ wmManipulatorMapType *mmap_type, const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ BLI_assert(wgt != NULL);
+ return WM_manipulatormaptype_group_link_ptr(mmap_type, wgt);
+}
+
+wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr(
+ wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
+{
+ wmManipulatorGroupTypeRef *wgt_ref = MEM_callocN(sizeof(wmManipulatorGroupTypeRef), "manipulator-group-ref");
+ wgt_ref->type = wgt;
+ BLI_addtail(&mmap_type->grouptype_refs, wgt_ref);
+ return wgt_ref;
+}
+
+void WM_manipulatormaptype_group_init_runtime_keymap(
+ const Main *bmain,
+ wmManipulatorGroupType *wgt)
+{
+ /* init keymap - on startup there's an extra call to init keymaps for 'permanent' manipulator-groups */
+ wm_manipulatorgrouptype_setup_keymap(wgt, ((wmWindowManager *)bmain->wm.first)->defaultconf);
+}
+
+void WM_manipulatormaptype_group_init_runtime(
+ const Main *bmain, wmManipulatorMapType *mmap_type,
+ wmManipulatorGroupType *wgt)
+{
+ /* now create a manipulator for all existing areas */
+ for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
+ for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
+ for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
+ ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
+ for (ARegion *ar = lb->first; ar; ar = ar->next) {
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ if (mmap && mmap->type == mmap_type) {
+ wm_manipulatorgroup_new_from_type(mmap, wgt);
+
+ /* just add here, drawing will occur on next update */
+ wm_manipulatormap_highlight_set(mmap, NULL, NULL, 0);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Unlike #WM_manipulatormaptype_group_unlink this doesn't maintain correct state, simply free.
+ */
+void WM_manipulatormaptype_group_free(wmManipulatorGroupTypeRef *wgt_ref)
+{
+ MEM_freeN(wgt_ref);
+}
+
+void WM_manipulatormaptype_group_unlink(
+ bContext *C, Main *bmain, wmManipulatorMapType *mmap_type,
+ const wmManipulatorGroupType *wgt)
+{
+ /* Free instances. */
+ for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
+ for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
+ for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
+ ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
+ for (ARegion *ar = lb->first; ar; ar = ar->next) {
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ if (mmap && mmap->type == mmap_type) {
+ wmManipulatorGroup *mgroup, *mgroup_next;
+ for (mgroup = mmap->groups.first; mgroup; mgroup = mgroup_next) {
+ mgroup_next = mgroup->next;
+ if (mgroup->type == wgt) {
+ BLI_assert(mgroup->parent_mmap == mmap);
+ wm_manipulatorgroup_free(C, mgroup);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Free types. */
+ wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt);
+ if (wgt_ref) {
+ BLI_remlink(&mmap_type->grouptype_refs, wgt_ref);
+ WM_manipulatormaptype_group_free(wgt_ref);
+ }
+
+ /* Note, we may want to keep this keymap for editing */
+ WM_keymap_remove(wgt->keyconf, wgt->keymap);
+
+ BLI_assert(WM_manipulatormaptype_group_find_ptr(mmap_type, wgt) == NULL);
+}
+
+void wm_manipulatorgrouptype_setup_keymap(
+ wmManipulatorGroupType *wgt, wmKeyConfig *keyconf)
+{
+ /* Use flag since setup_keymap may return NULL,
+ * in that case we better not keep calling it. */
+ if (wgt->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) {
+ wgt->keymap = wgt->setup_keymap(wgt, keyconf);
+ wgt->keyconf = keyconf;
+ wgt->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
+ }
+}
+
+/** \} */ /* wmManipulatorGroupType */
+
+/* -------------------------------------------------------------------- */
+/** \name High Level Add/Remove API
+ *
+ * For use directly from operators & RNA registration.
+ *
+ * \note In context of manipulator API these names are a bit misleading,
+ * but for general use terms its OK.
+ * `WM_manipulator_group_type_add` would be more correctly called:
+ * `WM_manipulatormaptype_grouptype_reference_link`
+ * but for general purpose API this is too detailed & annoying.
+ *
+ * \note We may want to return a value if there is nothing to remove.
+ *
+ * \{ */
+
+void WM_manipulator_group_type_add_ptr_ex(
+ wmManipulatorGroupType *wgt,
+ wmManipulatorMapType *mmap_type)
+{
+ WM_manipulatormaptype_group_link_ptr(mmap_type, wgt);
+
+ WM_manipulatorconfig_update_tag_init(mmap_type, wgt);
+}
+void WM_manipulator_group_type_add_ptr(
+ wmManipulatorGroupType *wgt)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
+ WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type);
+}
+void WM_manipulator_group_type_add(const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ BLI_assert(wgt != NULL);
+ WM_manipulator_group_type_add_ptr(wgt);
+}
+
+void WM_manipulator_group_type_ensure_ptr_ex(
+ wmManipulatorGroupType *wgt,
+ wmManipulatorMapType *mmap_type)
+{
+ wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt);
+ if (wgt_ref == NULL) {
+ WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type);
+ }
+}
+void WM_manipulator_group_type_ensure_ptr(
+ wmManipulatorGroupType *wgt)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
+ WM_manipulator_group_type_ensure_ptr_ex(wgt, mmap_type);
+}
+void WM_manipulator_group_type_ensure(const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ BLI_assert(wgt != NULL);
+ WM_manipulator_group_type_ensure_ptr(wgt);
+}
+
+void WM_manipulator_group_type_remove_ptr_ex(
+ struct Main *bmain, wmManipulatorGroupType *wgt,
+ wmManipulatorMapType *mmap_type)
+{
+ WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt);
+ WM_manipulatorgrouptype_free_ptr(wgt);
+}
+void WM_manipulator_group_type_remove_ptr(
+ struct Main *bmain, wmManipulatorGroupType *wgt)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
+ WM_manipulator_group_type_remove_ptr_ex(bmain, wgt, mmap_type);
+}
+void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ BLI_assert(wgt != NULL);
+ WM_manipulator_group_type_remove_ptr(bmain, wgt);
+}
+
+/* delayed versions */
+
+void WM_manipulator_group_type_unlink_delayed_ptr_ex(
+ wmManipulatorGroupType *wgt,
+ wmManipulatorMapType *mmap_type)
+{
+ WM_manipulatorconfig_update_tag_remove(mmap_type, wgt);
+}
+
+void WM_manipulator_group_type_unlink_delayed_ptr(
+ wmManipulatorGroupType *wgt)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
+ WM_manipulator_group_type_unlink_delayed_ptr_ex(wgt, mmap_type);
+}
+
+void WM_manipulator_group_type_unlink_delayed(const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ BLI_assert(wgt != NULL);
+ WM_manipulator_group_type_unlink_delayed_ptr(wgt);
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
new file mode 100644
index 00000000000..f93beb0ac9f
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
@@ -0,0 +1,197 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmo/intern/wm_gizmo_group_type.c
+ * \ingroup wm
+ */
+
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+#include "BLI_string.h"
+#include "BLI_string_utils.h"
+
+#include "BKE_context.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+/* only for own init/exit calls (wm_manipulatorgrouptype_init/wm_manipulatorgrouptype_free) */
+#include "wm.h"
+
+/* own includes */
+#include "wm_gizmo_wmapi.h"
+#include "wm_gizmo_intern.h"
+
+
+/** \name ManipulatorGroup Type Append
+ *
+ * \note This follows conventions from #WM_operatortype_find #WM_operatortype_append & friends.
+ * \{ */
+
+static GHash *global_manipulatorgrouptype_hash = NULL;
+
+wmManipulatorGroupType *WM_manipulatorgrouptype_find(const char *idname, bool quiet)
+{
+ if (idname[0]) {
+ wmManipulatorGroupType *wgt;
+
+ wgt = BLI_ghash_lookup(global_manipulatorgrouptype_hash, idname);
+ if (wgt) {
+ return wgt;
+ }
+
+ if (!quiet) {
+ printf("search for unknown manipulator group '%s'\n", idname);
+ }
+ }
+ else {
+ if (!quiet) {
+ printf("search for empty manipulator group\n");
+ }
+ }
+
+ return NULL;
+}
+
+/* caller must free */
+void WM_manipulatorgrouptype_iter(GHashIterator *ghi)
+{
+ BLI_ghashIterator_init(ghi, global_manipulatorgrouptype_hash);
+}
+
+static wmManipulatorGroupType *wm_manipulatorgrouptype_append__begin(void)
+{
+ wmManipulatorGroupType *wgt = MEM_callocN(sizeof(wmManipulatorGroupType), "manipulatorgrouptype");
+
+ return wgt;
+}
+static void wm_manipulatorgrouptype_append__end(wmManipulatorGroupType *wgt)
+{
+ BLI_assert(wgt->name != NULL);
+ BLI_assert(wgt->idname != NULL);
+
+ wgt->type_update_flag |= WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
+
+ /* if not set, use default */
+ if (wgt->setup_keymap == NULL) {
+ if (wgt->flag & WM_MANIPULATORGROUPTYPE_SELECT) {
+ wgt->setup_keymap = WM_manipulatorgroup_keymap_common_select;
+ }
+ else {
+ wgt->setup_keymap = WM_manipulatorgroup_keymap_common;
+ }
+ }
+
+ BLI_ghash_insert(global_manipulatorgrouptype_hash, (void *)wgt->idname, wgt);
+}
+
+wmManipulatorGroupType *WM_manipulatorgrouptype_append(
+ void (*wtfunc)(struct wmManipulatorGroupType *))
+{
+ wmManipulatorGroupType *wgt = wm_manipulatorgrouptype_append__begin();
+ wtfunc(wgt);
+ wm_manipulatorgrouptype_append__end(wgt);
+ return wgt;
+}
+
+wmManipulatorGroupType *WM_manipulatorgrouptype_append_ptr(
+ void (*wtfunc)(struct wmManipulatorGroupType *, void *), void *userdata)
+{
+ wmManipulatorGroupType *wgt = wm_manipulatorgrouptype_append__begin();
+ wtfunc(wgt, userdata);
+ wm_manipulatorgrouptype_append__end(wgt);
+ return wgt;
+}
+
+/**
+ * Append and insert into a manipulator typemap.
+ * This is most common for C manipulators which are enabled by default.
+ */
+wmManipulatorGroupTypeRef *WM_manipulatorgrouptype_append_and_link(
+ wmManipulatorMapType *mmap_type,
+ void (*wtfunc)(struct wmManipulatorGroupType *))
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_append(wtfunc);
+
+ wgt->mmap_params.spaceid = mmap_type->spaceid;
+ wgt->mmap_params.regionid = mmap_type->regionid;
+
+ return WM_manipulatormaptype_group_link_ptr(mmap_type, wgt);
+}
+
+/**
+ * Free but don't remove from ghash.
+ */
+static void manipulatorgrouptype_free(wmManipulatorGroupType *wgt)
+{
+ if (wgt->ext.srna) { /* python manipulator group, allocs own string */
+ MEM_freeN((void *)wgt->idname);
+ }
+
+ MEM_freeN(wgt);
+}
+
+void WM_manipulatorgrouptype_free_ptr(wmManipulatorGroupType *wgt)
+{
+ BLI_assert(wgt == WM_manipulatorgrouptype_find(wgt->idname, false));
+
+ BLI_ghash_remove(global_manipulatorgrouptype_hash, wgt->idname, NULL, NULL);
+
+ manipulatorgrouptype_free(wgt);
+
+ /* XXX, TODO, update the world! */
+}
+
+bool WM_manipulatorgrouptype_free(const char *idname)
+{
+ wmManipulatorGroupType *wgt = BLI_ghash_lookup(global_manipulatorgrouptype_hash, idname);
+
+ if (wgt == NULL) {
+ return false;
+ }
+
+ WM_manipulatorgrouptype_free_ptr(wgt);
+
+ return true;
+}
+
+static void wm_manipulatorgrouptype_ghash_free_cb(wmManipulatorGroupType *wgt)
+{
+ manipulatorgrouptype_free(wgt);
+}
+
+void wm_manipulatorgrouptype_free(void)
+{
+ BLI_ghash_free(global_manipulatorgrouptype_hash, NULL, (GHashValFreeFP)wm_manipulatorgrouptype_ghash_free_cb);
+ global_manipulatorgrouptype_hash = NULL;
+}
+
+/* called on initialize WM_init() */
+void wm_manipulatorgrouptype_init(void)
+{
+ /* reserve size is set based on blender default setup */
+ global_manipulatorgrouptype_hash = BLI_ghash_str_new_ex("wm_manipulatorgrouptype_init gh", 128);
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h b/source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h
new file mode 100644
index 00000000000..7a007a8a909
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h
@@ -0,0 +1,144 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmo/intern/wm_gizmo_intern.h
+ * \ingroup wm
+ */
+
+
+#ifndef __WM_GIZMO_INTERN_H__
+#define __WM_GIZMO_INTERN_H__
+
+struct wmKeyConfig;
+struct wmManipulatorMap;
+struct ManipulatorGeomInfo;
+struct GHashIterator;
+
+#include "wm_gizmo_fn.h"
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+
+bool wm_manipulator_select_set_ex(
+ struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select,
+ bool use_array, bool use_callback);
+bool wm_manipulator_select_and_highlight(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
+
+void wm_manipulator_calculate_scale(struct wmManipulator *mpr, const bContext *C);
+void wm_manipulator_update(struct wmManipulator *mpr, const bContext *C, const bool refresh_map);
+
+int wm_manipulator_is_visible(struct wmManipulator *mpr);
+enum {
+ WM_MANIPULATOR_IS_VISIBLE_UPDATE = (1 << 0),
+ WM_MANIPULATOR_IS_VISIBLE_DRAW = (1 << 1),
+};
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+enum {
+ TWEAK_MODAL_CANCEL = 1,
+ TWEAK_MODAL_CONFIRM,
+ TWEAK_MODAL_PRECISION_ON,
+ TWEAK_MODAL_PRECISION_OFF,
+ TWEAK_MODAL_SNAP_ON,
+ TWEAK_MODAL_SNAP_OFF,
+};
+
+struct wmManipulatorGroup *wm_manipulatorgroup_new_from_type(
+ struct wmManipulatorMap *mmap, struct wmManipulatorGroupType *wgt);
+void wm_manipulatorgroup_free(bContext *C, struct wmManipulatorGroup *mgroup);
+void wm_manipulatorgroup_manipulator_register(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr);
+struct wmManipulator *wm_manipulatorgroup_find_intersected_manipulator(
+ const struct wmManipulatorGroup *mgroup, struct bContext *C, const struct wmEvent *event,
+ int *r_part);
+void wm_manipulatorgroup_intersectable_manipulators_to_list(
+ const struct wmManipulatorGroup *mgroup, struct ListBase *listbase);
+void wm_manipulatorgroup_ensure_initialized(struct wmManipulatorGroup *mgroup, const struct bContext *C);
+bool wm_manipulatorgroup_is_visible_in_drawstep(
+ const struct wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep);
+
+void wm_manipulatorgrouptype_setup_keymap(
+ struct wmManipulatorGroupType *wgt, struct wmKeyConfig *keyconf);
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+typedef struct wmManipulatorMapSelectState {
+ struct wmManipulator **items;
+ int len, len_alloc;
+} wmManipulatorMapSelectState;
+
+struct wmManipulatorMap {
+
+ struct wmManipulatorMapType *type;
+ ListBase groups; /* wmManipulatorGroup */
+
+ /* private, update tagging (enum defined in C source). */
+ char update_flag[WM_MANIPULATORMAP_DRAWSTEP_MAX];
+
+ /**
+ * \brief Manipulator map runtime context
+ *
+ * Contains information about this manipulator-map. Currently
+ * highlighted manipulator, currently selected manipulators, ...
+ */
+ struct {
+ /* we redraw the manipulator-map when this changes */
+ struct wmManipulator *highlight;
+ /* User has clicked this manipulator and it gets all input. */
+ struct wmManipulator *modal;
+ /* array for all selected manipulators */
+ struct wmManipulatorMapSelectState select;
+ /* cursor location at point of entering modal (see: WM_MANIPULATOR_GRAB_CURSOR) */
+ int event_xy[2];
+ short event_grabcursor;
+ /* until we have nice cursor push/pop API. */
+ int last_cursor;
+ } mmap_context;
+};
+
+/**
+ * This is a container for all manipulator types that can be instantiated in a region.
+ * (similar to dropboxes).
+ *
+ * \note There is only ever one of these for every (area, region) combination.
+ */
+struct wmManipulatorMapType {
+ struct wmManipulatorMapType *next, *prev;
+ short spaceid, regionid;
+ /* types of manipulator-groups for this manipulator-map type */
+ ListBase grouptype_refs;
+
+ /* eManipulatorMapTypeUpdateFlags */
+ eWM_ManipulatorMapTypeUpdateFlag type_update_flag;
+};
+
+void wm_manipulatormap_select_array_clear(struct wmManipulatorMap *mmap);
+bool wm_manipulatormap_deselect_all(struct wmManipulatorMap *mmap);
+void wm_manipulatormap_select_array_shrink(struct wmManipulatorMap *mmap, int len_subtract);
+void wm_manipulatormap_select_array_push_back(struct wmManipulatorMap *mmap, wmManipulator *mpr);
+void wm_manipulatormap_select_array_remove(struct wmManipulatorMap *mmap, wmManipulator *mpr);
+
+#endif
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
new file mode 100644
index 00000000000..94db6b45887
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -0,0 +1,1209 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+ * \ingroup wm
+ */
+
+#include <string.h>
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_rect.h"
+#include "BLI_string.h"
+#include "BLI_ghash.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "GPU_glew.h"
+#include "GPU_matrix.h"
+#include "GPU_select.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "wm_event_system.h"
+
+/* for tool-tips */
+#include "UI_interface.h"
+
+#include "DEG_depsgraph.h"
+
+/* own includes */
+#include "wm_gizmo_wmapi.h"
+#include "wm_gizmo_intern.h"
+
+/**
+ * Store all manipulator-maps here. Anyone who wants to register a manipulator for a certain
+ * area type can query the manipulator-map to do so.
+ */
+static ListBase manipulatormaptypes = {NULL, NULL};
+
+/**
+ * Update when manipulator-map types change.
+ */
+/* so operator removal can trigger update */
+typedef enum eWM_ManipulatorGroupTypeGlobalFlag {
+ WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0),
+ WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1),
+} eWM_ManipulatorGroupTypeGlobalFlag;
+
+static eWM_ManipulatorGroupTypeGlobalFlag wm_mmap_type_update_flag = 0;
+
+/**
+ * Manipulator-map update tagging.
+ */
+enum {
+ /** #manipulatormap_prepare_drawing has run */
+ MANIPULATORMAP_IS_PREPARE_DRAW = (1 << 0),
+ MANIPULATORMAP_IS_REFRESH_CALLBACK = (1 << 1),
+};
+
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorMap Selection Array API
+ *
+ * Just handle ``wm_manipulatormap_select_array_*``, not flags or callbacks.
+ *
+ * \{ */
+
+static void wm_manipulatormap_select_array_ensure_len_alloc(wmManipulatorMap *mmap, int len)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ if (len <= msel->len_alloc) {
+ return;
+ }
+ msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len);
+ msel->len_alloc = len;
+}
+
+void wm_manipulatormap_select_array_clear(wmManipulatorMap *mmap)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ MEM_SAFE_FREE(msel->items);
+ msel->len = 0;
+ msel->len_alloc = 0;
+}
+
+void wm_manipulatormap_select_array_shrink(wmManipulatorMap *mmap, int len_subtract)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ msel->len -= len_subtract;
+ if (msel->len <= 0) {
+ wm_manipulatormap_select_array_clear(mmap);
+ }
+ else {
+ if (msel->len < msel->len_alloc / 2) {
+ msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len);
+ msel->len_alloc = msel->len;
+ }
+ }
+}
+
+void wm_manipulatormap_select_array_push_back(wmManipulatorMap *mmap, wmManipulator *mpr)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ BLI_assert(msel->len <= msel->len_alloc);
+ if (msel->len == msel->len_alloc) {
+ msel->len_alloc = (msel->len + 1) * 2;
+ msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len_alloc);
+ }
+ msel->items[msel->len++] = mpr;
+}
+
+void wm_manipulatormap_select_array_remove(wmManipulatorMap *mmap, wmManipulator *mpr)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ /* remove manipulator from selected_manipulators array */
+ for (int i = 0; i < msel->len; i++) {
+ if (msel->items[i] == mpr) {
+ for (int j = i; j < (msel->len - 1); j++) {
+ msel->items[j] = msel->items[j + 1];
+ }
+ wm_manipulatormap_select_array_shrink(mmap, 1);
+ break;
+ }
+ }
+
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorMap
+ *
+ * \{ */
+
+/**
+ * Creates a manipulator-map with all registered manipulators for that type
+ */
+wmManipulatorMap *WM_manipulatormap_new_from_type(
+ const struct wmManipulatorMapType_Params *mmap_params)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(mmap_params);
+ wmManipulatorMap *mmap;
+
+ mmap = MEM_callocN(sizeof(wmManipulatorMap), "ManipulatorMap");
+ mmap->type = mmap_type;
+ WM_manipulatormap_tag_refresh(mmap);
+
+ /* create all manipulator-groups for this manipulator-map. We may create an empty one
+ * too in anticipation of manipulators from operators etc */
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) {
+ wm_manipulatorgroup_new_from_type(mmap, wgt_ref->type);
+ }
+
+ return mmap;
+}
+
+void wm_manipulatormap_remove(wmManipulatorMap *mmap)
+{
+ /* Clear first so further calls don't waste time trying to maintain correct array state. */
+ wm_manipulatormap_select_array_clear(mmap);
+
+ for (wmManipulatorGroup *mgroup = mmap->groups.first, *mgroup_next; mgroup; mgroup = mgroup_next) {
+ mgroup_next = mgroup->next;
+ BLI_assert(mgroup->parent_mmap == mmap);
+ wm_manipulatorgroup_free(NULL, mgroup);
+ }
+ BLI_assert(BLI_listbase_is_empty(&mmap->groups));
+
+ MEM_freeN(mmap);
+}
+
+
+wmManipulatorGroup *WM_manipulatormap_group_find(
+ struct wmManipulatorMap *mmap,
+ const char *idname)
+{
+ wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
+ if (wgt) {
+ return WM_manipulatormap_group_find_ptr(mmap, wgt);
+ }
+ return NULL;
+}
+
+wmManipulatorGroup *WM_manipulatormap_group_find_ptr(
+ struct wmManipulatorMap *mmap,
+ const struct wmManipulatorGroupType *wgt)
+{
+ for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+ if (mgroup->type == wgt) {
+ return mgroup;
+ }
+ }
+ return NULL;
+}
+
+const ListBase *WM_manipulatormap_group_list(wmManipulatorMap *mmap)
+{
+ return &mmap->groups;
+}
+
+bool WM_manipulatormap_is_any_selected(const wmManipulatorMap *mmap)
+{
+ return mmap->mmap_context.select.len != 0;
+}
+
+/**
+ * \note We could use a callback to define bounds, for now just use matrix location.
+ */
+bool WM_manipulatormap_minmax(
+ const wmManipulatorMap *mmap, bool UNUSED(use_hidden), bool use_select,
+ float r_min[3], float r_max[3])
+{
+ if (use_select) {
+ int i;
+ for (i = 0; i < mmap->mmap_context.select.len; i++) {
+ minmax_v3v3_v3(r_min, r_max, mmap->mmap_context.select.items[i]->matrix_basis[3]);
+ }
+ return i != 0;
+ }
+ else {
+ bool ok = false;
+ BLI_assert(!"TODO");
+ return ok;
+ }
+}
+
+/**
+ * Creates and returns idname hash table for (visible) manipulators in \a mmap
+ *
+ * \param poll Polling function for excluding manipulators.
+ * \param data Custom data passed to \a poll
+ *
+ * TODO(campbell): this uses unreliable order,
+ * best we use an iterator function instead of a hash.
+ */
+static GHash *WM_manipulatormap_manipulator_hash_new(
+ const bContext *C, wmManipulatorMap *mmap,
+ bool (*poll)(const wmManipulator *, void *),
+ void *data, const bool include_hidden)
+{
+ GHash *hash = BLI_ghash_ptr_new(__func__);
+
+ /* collect manipulators */
+ for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+ if (WM_manipulator_group_type_poll(C, mgroup->type)) {
+ for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ if ((include_hidden || (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) &&
+ (!poll || poll(mpr, data)))
+ {
+ BLI_ghash_insert(hash, mpr, mpr);
+ }
+ }
+ }
+ }
+
+ return hash;
+}
+
+void WM_manipulatormap_tag_refresh(wmManipulatorMap *mmap)
+{
+ if (mmap) {
+ /* We might want only to refresh some, for tag all steps. */
+ for (int i = 0; i < WM_MANIPULATORMAP_DRAWSTEP_MAX; i++) {
+ mmap->update_flag[i] |= (
+ MANIPULATORMAP_IS_PREPARE_DRAW |
+ MANIPULATORMAP_IS_REFRESH_CALLBACK);
+ }
+ }
+}
+
+static bool manipulator_prepare_drawing(
+ wmManipulatorMap *mmap, wmManipulator *mpr,
+ const bContext *C, ListBase *draw_manipulators,
+ const eWM_ManipulatorMapDrawStep drawstep)
+{
+ int do_draw = wm_manipulator_is_visible(mpr);
+ if (do_draw == 0) {
+ /* skip */
+ }
+ else {
+ /* Ensure we get RNA updates */
+ if (do_draw & WM_MANIPULATOR_IS_VISIBLE_UPDATE) {
+ /* hover manipulators need updating, even if we don't draw them */
+ wm_manipulator_update(mpr, C, (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_PREPARE_DRAW) != 0);
+ }
+ if (do_draw & WM_MANIPULATOR_IS_VISIBLE_DRAW) {
+ BLI_addhead(draw_manipulators, BLI_genericNodeN(mpr));
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Update manipulators of \a mmap to prepare for drawing. Adds all manipulators that
+ * should be drawn to list \a draw_manipulators, note that added items need freeing.
+ */
+static void manipulatormap_prepare_drawing(
+ wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators,
+ const eWM_ManipulatorMapDrawStep drawstep)
+{
+ if (!mmap || BLI_listbase_is_empty(&mmap->groups))
+ return;
+ wmManipulator *mpr_modal = mmap->mmap_context.modal;
+
+ /* only active manipulator needs updating */
+ if (mpr_modal) {
+ if ((mpr_modal->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL) == 0) {
+ if (wm_manipulatorgroup_is_visible_in_drawstep(mpr_modal->parent_mgroup, drawstep)) {
+ if (manipulator_prepare_drawing(mmap, mpr_modal, C, draw_manipulators, drawstep)) {
+ mmap->update_flag[drawstep] &= ~MANIPULATORMAP_IS_PREPARE_DRAW;
+ }
+ }
+ /* don't draw any other manipulators */
+ return;
+ }
+ }
+
+ for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+ /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */
+ if (!wm_manipulatorgroup_is_visible_in_drawstep(mgroup, drawstep) ||
+ !WM_manipulator_group_type_poll(C, mgroup->type))
+ {
+ continue;
+ }
+
+ /* needs to be initialized on first draw */
+ /* XXX weak: Manipulator-group may skip refreshing if it's invisible (map gets untagged nevertheless) */
+ if (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_REFRESH_CALLBACK) {
+ /* force refresh again. */
+ mgroup->init_flag &= ~WM_MANIPULATORGROUP_INIT_REFRESH;
+ }
+ /* Calls `setup`, `setup_keymap` and `refresh` if they're defined. */
+ wm_manipulatorgroup_ensure_initialized(mgroup, C);
+
+ /* prepare drawing */
+ if (mgroup->type->draw_prepare) {
+ mgroup->type->draw_prepare(C, mgroup);
+ }
+
+ for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ manipulator_prepare_drawing(mmap, mpr, C, draw_manipulators, drawstep);
+ }
+ }
+
+ mmap->update_flag[drawstep] &=
+ ~(MANIPULATORMAP_IS_REFRESH_CALLBACK |
+ MANIPULATORMAP_IS_PREPARE_DRAW);
+}
+
+/**
+ * Draw all visible manipulators in \a mmap.
+ * Uses global draw_manipulators listbase.
+ */
+static void manipulators_draw_list(const wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators)
+{
+ /* Can be empty if we're dynamically added and removed. */
+ if ((mmap == NULL) || BLI_listbase_is_empty(&mmap->groups)) {
+ return;
+ }
+
+ /* TODO this will need it own shader probably? don't think it can be handled from that point though. */
+/* const bool use_lighting = (U.manipulator_flag & V3D_MANIPULATOR_SHADED) != 0; */
+
+ bool is_depth_prev = false;
+
+ /* draw_manipulators contains all visible manipulators - draw them */
+ for (LinkData *link = draw_manipulators->first, *link_next; link; link = link_next) {
+ wmManipulator *mpr = link->data;
+ link_next = link->next;
+
+ bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0;
+
+ /* Weak! since we don't 100% support depth yet (select ignores depth) always show highlighted */
+ if (is_depth && (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT)) {
+ is_depth = false;
+ }
+
+ if (is_depth == is_depth_prev) {
+ /* pass */
+ }
+ else {
+ if (is_depth) {
+ glEnable(GL_DEPTH_TEST);
+ }
+ else {
+ glDisable(GL_DEPTH_TEST);
+ }
+ is_depth_prev = is_depth;
+ }
+
+ /* XXX force AntiAlias Manipulators. */
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_POLYGON_SMOOTH);
+
+ mpr->type->draw(C, mpr);
+
+ glDisable(GL_LINE_SMOOTH);
+ glDisable(GL_POLYGON_SMOOTH);
+
+ /* free/remove manipulator link after drawing */
+ BLI_freelinkN(draw_manipulators, link);
+ }
+
+ if (is_depth_prev) {
+ glDisable(GL_DEPTH_TEST);
+ }
+}
+
+void WM_manipulatormap_draw(
+ wmManipulatorMap *mmap, const bContext *C,
+ const eWM_ManipulatorMapDrawStep drawstep)
+{
+ if (!WM_manipulator_context_check_drawstep(C, drawstep)) {
+ return;
+ }
+
+ ListBase draw_manipulators = {NULL};
+
+ manipulatormap_prepare_drawing(mmap, C, &draw_manipulators, drawstep);
+ manipulators_draw_list(mmap, C, &draw_manipulators);
+ BLI_assert(BLI_listbase_is_empty(&draw_manipulators));
+}
+
+static void manipulator_draw_select_3D_loop(const bContext *C, ListBase *visible_manipulators)
+{
+ int select_id = 0;
+ wmManipulator *mpr;
+
+ /* TODO(campbell): this depends on depth buffer being written to, currently broken for the 3D view. */
+ bool is_depth_prev = false;
+ bool is_depth_skip_prev = false;
+
+ for (LinkData *link = visible_manipulators->first; link; link = link->next) {
+ mpr = link->data;
+
+ bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0;
+ if (is_depth == is_depth_prev) {
+ /* pass */
+ }
+ else {
+ if (is_depth) {
+ glEnable(GL_DEPTH_TEST);
+ }
+ else {
+ glDisable(GL_DEPTH_TEST);
+ }
+ is_depth_prev = is_depth;
+ }
+ bool is_depth_skip = (mpr->flag & WM_MANIPULATOR_SELECT_BACKGROUND) != 0;
+ if (is_depth_skip == is_depth_skip_prev) {
+ /* pass */
+ }
+ else {
+ glDepthMask(!is_depth_skip);
+ is_depth_skip_prev = is_depth_skip;
+ }
+
+ /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected manipulator part id */
+
+ mpr->type->draw_select(C, mpr, select_id << 8);
+
+
+ select_id++;
+ }
+
+ if (is_depth_prev) {
+ glDisable(GL_DEPTH_TEST);
+ }
+ if (is_depth_skip_prev) {
+ glDepthMask(true);
+ }
+}
+
+static int manipulator_find_intersected_3d_intern(
+ ListBase *visible_manipulators, const bContext *C, const int co[2],
+ const int hotspot)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *ar = CTX_wm_region(C);
+ View3D *v3d = sa->spacedata.first;
+ rcti rect;
+ /* Almost certainly overkill, but allow for many custom manipulators. */
+ GLuint buffer[MAXPICKBUF];
+ short hits;
+ const bool do_passes = GPU_select_query_check_active();
+
+ BLI_rcti_init_pt_radius(&rect, co, hotspot);
+
+ ED_view3d_draw_setup_view(CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, &rect);
+
+ if (do_passes)
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
+ else
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_ALL, 0);
+ /* do the drawing */
+ manipulator_draw_select_3D_loop(C, visible_manipulators);
+
+ hits = GPU_select_end();
+
+ if (do_passes && (hits > 0)) {
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
+ manipulator_draw_select_3D_loop(C, visible_manipulators);
+ GPU_select_end();
+ }
+
+ ED_view3d_draw_setup_view(CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, NULL);
+
+ const GLuint *hit_near = GPU_select_buffer_near(buffer, hits);
+
+ return hit_near ? hit_near[3] : -1;
+}
+
+/**
+ * Try to find a 3D manipulator at screen-space coordinate \a co. Uses OpenGL picking.
+ */
+static wmManipulator *manipulator_find_intersected_3d(
+ bContext *C, const int co[2], ListBase *visible_manipulators,
+ int *r_part)
+{
+ wmManipulator *result = NULL;
+ int hit = -1;
+
+ int hotspot_radii[] = {
+ 3 * U.pixelsize,
+ /* This runs on mouse move, careful doing too many tests! */
+ 10 * U.pixelsize,
+ };
+
+ *r_part = 0;
+
+ /* set up view matrices */
+ view3d_operator_needs_opengl(C);
+
+ hit = -1;
+
+ for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) {
+ hit = manipulator_find_intersected_3d_intern(visible_manipulators, C, co, hotspot_radii[i]);
+ if (hit != -1) {
+ break;
+ }
+ }
+
+ if (hit != -1) {
+ LinkData *link = BLI_findlink(visible_manipulators, hit >> 8);
+ if (link != NULL) {
+ *r_part = hit & 255;
+ result = link->data;
+ }
+ else {
+ /* All manipulators should use selection ID they're given as part of the callback,
+ * if they don't it will attempt tp lookup non-existing index. */
+ BLI_assert(0);
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Try to find a manipulator under the mouse position. 2D intersections have priority over
+ * 3D ones (could check for smallest screen-space distance but not needed right now).
+ */
+wmManipulator *wm_manipulatormap_highlight_find(
+ wmManipulatorMap *mmap, bContext *C, const wmEvent *event,
+ int *r_part)
+{
+ wmManipulator *mpr = NULL;
+ ListBase visible_3d_manipulators = {NULL};
+ bool do_step[WM_MANIPULATORMAP_DRAWSTEP_MAX];
+
+ for (int i = 0; i < ARRAY_SIZE(do_step); i++) {
+ do_step[i] = WM_manipulator_context_check_drawstep(C, i);
+ }
+
+ for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+
+ /* If it were important we could initialize here,
+ * but this only happens when events are handled before drawing,
+ * just skip to keep code-path for initializing manipulators simple. */
+ if ((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0) {
+ continue;
+ }
+
+ if (WM_manipulator_group_type_poll(C, mgroup->type)) {
+ eWM_ManipulatorMapDrawStep step;
+ if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) {
+ step = WM_MANIPULATORMAP_DRAWSTEP_3D;
+ }
+ else {
+ step = WM_MANIPULATORMAP_DRAWSTEP_2D;
+ }
+
+ if (do_step[step]) {
+ if ((mmap->update_flag[step] & MANIPULATORMAP_IS_REFRESH_CALLBACK) &&
+ (mgroup->type->refresh != NULL))
+ {
+ mgroup->type->refresh(C, mgroup);
+ /* cleared below */
+ }
+ if (step == WM_MANIPULATORMAP_DRAWSTEP_3D) {
+ wm_manipulatorgroup_intersectable_manipulators_to_list(mgroup, &visible_3d_manipulators);
+ }
+ else if (step == WM_MANIPULATORMAP_DRAWSTEP_2D) {
+ if ((mpr = wm_manipulatorgroup_find_intersected_manipulator(mgroup, C, event, r_part))) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (!BLI_listbase_is_empty(&visible_3d_manipulators)) {
+ /* 2D manipulators get priority. */
+ if (mpr == NULL) {
+ mpr = manipulator_find_intersected_3d(C, event->mval, &visible_3d_manipulators, r_part);
+ }
+ BLI_freelistN(&visible_3d_manipulators);
+ }
+
+ mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_3D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK;
+ mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_2D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK;
+
+ return mpr;
+}
+
+void WM_manipulatormap_add_handlers(ARegion *ar, wmManipulatorMap *mmap)
+{
+ wmEventHandler *handler;
+
+ for (handler = ar->handlers.first; handler; handler = handler->next) {
+ if (handler->manipulator_map == mmap) {
+ return;
+ }
+ }
+
+ handler = MEM_callocN(sizeof(wmEventHandler), "manipulator handler");
+
+ BLI_assert(mmap == ar->manipulator_map);
+ handler->manipulator_map = mmap;
+ BLI_addtail(&ar->handlers, handler);
+}
+
+void wm_manipulatormaps_handled_modal_update(
+ bContext *C, wmEvent *event, wmEventHandler *handler)
+{
+ const bool modal_running = (handler->op != NULL);
+
+ /* happens on render or when joining areas */
+ if (!handler->op_region || !handler->op_region->manipulator_map) {
+ return;
+ }
+
+ wmManipulatorMap *mmap = handler->op_region->manipulator_map;
+ wmManipulator *mpr = wm_manipulatormap_modal_get(mmap);
+ ScrArea *area = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
+
+ wm_manipulatormap_handler_context(C, handler);
+
+ /* regular update for running operator */
+ if (modal_running) {
+ wmManipulatorOpElem *mpop = mpr ? WM_manipulator_operator_get(mpr, mpr->highlight_part) : NULL;
+ if (mpr && mpop && (mpop->type != NULL) && (mpop->type == handler->op->type)) {
+ wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal;
+ if (modal_fn != NULL) {
+ int retval = modal_fn(C, mpr, event, 0);
+ /* The manipulator is tried to the operator, we can't choose when to exit. */
+ BLI_assert(retval & OPERATOR_RUNNING_MODAL);
+ UNUSED_VARS_NDEBUG(retval);
+ }
+ }
+ }
+ /* operator not running anymore */
+ else {
+ wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
+ if (mpr) {
+ /* This isn't defined if it ends because of success of cancel, we may want to change. */
+ bool cancel = true;
+ if (mpr->type->exit) {
+ mpr->type->exit(C, mpr, cancel);
+ }
+ wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false);
+ }
+ }
+
+ /* restore the area */
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+}
+
+/**
+ * Deselect all selected manipulators in \a mmap.
+ * \return if selection has changed.
+ */
+bool wm_manipulatormap_deselect_all(wmManipulatorMap *mmap)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+
+ if (msel->items == NULL || msel->len == 0) {
+ return false;
+ }
+
+ for (int i = 0; i < msel->len; i++) {
+ wm_manipulator_select_set_ex(mmap, msel->items[i], false, false, true);
+ }
+
+ wm_manipulatormap_select_array_clear(mmap);
+
+ /* always return true, we already checked
+ * if there's anything to deselect */
+ return true;
+}
+
+BLI_INLINE bool manipulator_selectable_poll(const wmManipulator *mpr, void *UNUSED(data))
+{
+ return (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT);
+}
+
+/**
+ * Select all selectable manipulators in \a mmap.
+ * \return if selection has changed.
+ */
+static bool wm_manipulatormap_select_all_intern(
+ bContext *C, wmManipulatorMap *mmap)
+{
+ wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
+ /* GHash is used here to avoid having to loop over all manipulators twice (once to
+ * get tot_sel for allocating, once for actually selecting). Instead we collect
+ * selectable manipulators in hash table and use this to get tot_sel and do selection */
+
+ GHash *hash = WM_manipulatormap_manipulator_hash_new(C, mmap, manipulator_selectable_poll, NULL, true);
+ GHashIterator gh_iter;
+ int i;
+ bool changed = false;
+
+ wm_manipulatormap_select_array_ensure_len_alloc(mmap, BLI_ghash_len(hash));
+
+ GHASH_ITER_INDEX (gh_iter, hash, i) {
+ wmManipulator *mpr_iter = BLI_ghashIterator_getValue(&gh_iter);
+ WM_manipulator_select_set(mmap, mpr_iter, true);
+ }
+ /* highlight first manipulator */
+ wm_manipulatormap_highlight_set(mmap, C, msel->items[0], msel->items[0]->highlight_part);
+
+ BLI_assert(BLI_ghash_len(hash) == msel->len);
+
+ BLI_ghash_free(hash, NULL, NULL);
+ return changed;
+}
+
+/**
+ * Select/Deselect all selectable manipulators in \a mmap.
+ * \return if selection has changed.
+ *
+ * TODO select all by type
+ */
+bool WM_manipulatormap_select_all(bContext *C, wmManipulatorMap *mmap, const int action)
+{
+ bool changed = false;
+
+ switch (action) {
+ case SEL_SELECT:
+ changed = wm_manipulatormap_select_all_intern(C, mmap);
+ break;
+ case SEL_DESELECT:
+ changed = wm_manipulatormap_deselect_all(mmap);
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+
+ if (changed)
+ WM_event_add_mousemove(C);
+
+ return changed;
+}
+
+/**
+ * Prepare context for manipulator handling (but only if area/region is
+ * part of screen). Version of #wm_handler_op_context for manipulators.
+ */
+void wm_manipulatormap_handler_context(bContext *C, wmEventHandler *handler)
+{
+ bScreen *screen = CTX_wm_screen(C);
+
+ if (screen) {
+ if (handler->op_area == NULL) {
+ /* do nothing in this context */
+ }
+ else {
+ ScrArea *sa;
+
+ for (sa = screen->areabase.first; sa; sa = sa->next)
+ if (sa == handler->op_area)
+ break;
+ if (sa == NULL) {
+ /* when changing screen layouts with running modal handlers (like render display), this
+ * is not an error to print */
+ if (handler->manipulator_map == NULL)
+ printf("internal error: modal manipulator-map handler has invalid area\n");
+ }
+ else {
+ ARegion *ar;
+ CTX_wm_area_set(C, sa);
+ for (ar = sa->regionbase.first; ar; ar = ar->next)
+ if (ar == handler->op_region)
+ break;
+ /* XXX no warning print here, after full-area and back regions are remade */
+ if (ar)
+ CTX_wm_region_set(C, ar);
+ }
+ }
+ }
+}
+
+bool WM_manipulatormap_cursor_set(const wmManipulatorMap *mmap, wmWindow *win)
+{
+ wmManipulator *mpr = mmap->mmap_context.highlight;
+ if (mpr && mpr->type->cursor_get) {
+ WM_cursor_set(win, mpr->type->cursor_get(mpr));
+ return true;
+ }
+
+ return false;
+}
+
+bool wm_manipulatormap_highlight_set(
+ wmManipulatorMap *mmap, const bContext *C, wmManipulator *mpr, int part)
+{
+ if ((mpr != mmap->mmap_context.highlight) ||
+ (mpr && part != mpr->highlight_part))
+ {
+ if (mmap->mmap_context.highlight) {
+ mmap->mmap_context.highlight->state &= ~WM_MANIPULATOR_STATE_HIGHLIGHT;
+ mmap->mmap_context.highlight->highlight_part = -1;
+ }
+
+ mmap->mmap_context.highlight = mpr;
+
+ if (mpr) {
+ mpr->state |= WM_MANIPULATOR_STATE_HIGHLIGHT;
+ mpr->highlight_part = part;
+ mmap->mmap_context.last_cursor = -1;
+
+ if (C && mpr->type->cursor_get) {
+ wmWindow *win = CTX_wm_window(C);
+ mmap->mmap_context.last_cursor = win->cursor;
+ WM_cursor_set(win, mpr->type->cursor_get(mpr));
+ }
+ }
+ else {
+ if (C && mmap->mmap_context.last_cursor != -1) {
+ wmWindow *win = CTX_wm_window(C);
+ WM_cursor_set(win, mmap->mmap_context.last_cursor);
+ }
+ }
+
+ /* tag the region for redraw */
+ if (C) {
+ ARegion *ar = CTX_wm_region(C);
+ ED_region_tag_redraw(ar);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+wmManipulator *wm_manipulatormap_highlight_get(wmManipulatorMap *mmap)
+{
+ return mmap->mmap_context.highlight;
+}
+
+/**
+ * Caller should call exit when (enable == False).
+ */
+void wm_manipulatormap_modal_set(
+ wmManipulatorMap *mmap, bContext *C, wmManipulator *mpr, const wmEvent *event, bool enable)
+{
+ if (enable) {
+ BLI_assert(mmap->mmap_context.modal == NULL);
+ wmWindow *win = CTX_wm_window(C);
+
+ WM_tooltip_clear(C, win);
+
+ if (mpr->type->invoke &&
+ (mpr->type->modal || mpr->custom_modal))
+ {
+ const int retval = mpr->type->invoke(C, mpr, event);
+ if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
+ return;
+ }
+ }
+
+ mpr->state |= WM_MANIPULATOR_STATE_MODAL;
+ mmap->mmap_context.modal = mpr;
+
+ if ((mpr->flag & WM_MANIPULATOR_GRAB_CURSOR) &&
+ (event->is_motion_absolute == false))
+ {
+ WM_cursor_grab_enable(win, true, true, NULL);
+ copy_v2_v2_int(mmap->mmap_context.event_xy, &event->x);
+ mmap->mmap_context.event_grabcursor = win->grabcursor;
+ }
+ else {
+ mmap->mmap_context.event_xy[0] = INT_MAX;
+ }
+
+ struct wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
+ if (mpop && mpop->type) {
+ const int retval = WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr);
+ if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
+ wm_manipulatormap_modal_set(mmap, C, mpr, event, false);
+ }
+
+ /* we failed to hook the manipulator to the operator handler or operator was cancelled, return */
+ if (!mmap->mmap_context.modal) {
+ mpr->state &= ~WM_MANIPULATOR_STATE_MODAL;
+ MEM_SAFE_FREE(mpr->interaction_data);
+ }
+ return;
+ }
+ }
+ else {
+ BLI_assert(ELEM(mmap->mmap_context.modal, NULL, mpr));
+
+ /* deactivate, manipulator but first take care of some stuff */
+ if (mpr) {
+ mpr->state &= ~WM_MANIPULATOR_STATE_MODAL;
+ MEM_SAFE_FREE(mpr->interaction_data);
+ }
+ mmap->mmap_context.modal = NULL;
+
+ if (C) {
+ wmWindow *win = CTX_wm_window(C);
+ if (mmap->mmap_context.event_xy[0] != INT_MAX) {
+ /* Check if some other part of Blender (typically operators)
+ * have adjusted the grab mode since it was set.
+ * If so: warp, so we have a predictable outcome. */
+ if (mmap->mmap_context.event_grabcursor == win->grabcursor) {
+ WM_cursor_grab_disable(win, mmap->mmap_context.event_xy);
+ }
+ else {
+ WM_cursor_warp(win, UNPACK2(mmap->mmap_context.event_xy));
+ }
+ }
+ ED_region_tag_redraw(CTX_wm_region(C));
+ WM_event_add_mousemove(C);
+ }
+
+ mmap->mmap_context.event_xy[0] = INT_MAX;
+ }
+}
+
+wmManipulator *wm_manipulatormap_modal_get(wmManipulatorMap *mmap)
+{
+ return mmap->mmap_context.modal;
+}
+
+wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len)
+{
+ *r_selected_len = mmap->mmap_context.select.len;
+ return mmap->mmap_context.select.items;
+}
+
+ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap)
+{
+ return &mmap->groups;
+}
+
+void WM_manipulatormap_message_subscribe(
+ bContext *C, wmManipulatorMap *mmap, ARegion *ar, struct wmMsgBus *mbus)
+{
+ for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+ if (!WM_manipulator_group_type_poll(C, mgroup->type)) {
+ continue;
+ }
+ for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
+ if (mpr->flag & WM_MANIPULATOR_HIDDEN) {
+ continue;
+ }
+ WM_manipulator_target_property_subscribe_all(mpr, mbus, ar);
+ }
+ if (mgroup->type->message_subscribe != NULL) {
+ mgroup->type->message_subscribe(C, mgroup, mbus);
+ }
+ }
+}
+
+/** \} */ /* wmManipulatorMap */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Tooltip Handling
+ *
+ * \{ */
+
+struct ARegion *WM_manipulatormap_tooltip_init(
+ struct bContext *C, struct ARegion *ar, bool *r_exit_on_event)
+{
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ *r_exit_on_event = true;
+ if (mmap) {
+ wmManipulator *mpr = mmap->mmap_context.highlight;
+ if (mpr) {
+ return UI_tooltip_create_from_manipulator(C, mpr);
+ }
+ }
+ return NULL;
+}
+
+/** \} */ /* wmManipulatorMapType */
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorMapType
+ *
+ * \{ */
+
+wmManipulatorMapType *WM_manipulatormaptype_find(
+ const struct wmManipulatorMapType_Params *mmap_params)
+{
+ for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) {
+ if (mmap_type->spaceid == mmap_params->spaceid &&
+ mmap_type->regionid == mmap_params->regionid)
+ {
+ return mmap_type;
+ }
+ }
+
+ return NULL;
+}
+
+wmManipulatorMapType *WM_manipulatormaptype_ensure(
+ const struct wmManipulatorMapType_Params *mmap_params)
+{
+ wmManipulatorMapType *mmap_type = WM_manipulatormaptype_find(mmap_params);
+
+ if (mmap_type) {
+ return mmap_type;
+ }
+
+ mmap_type = MEM_callocN(sizeof(wmManipulatorMapType), "manipulatortype list");
+ mmap_type->spaceid = mmap_params->spaceid;
+ mmap_type->regionid = mmap_params->regionid;
+ BLI_addhead(&manipulatormaptypes, mmap_type);
+
+ return mmap_type;
+}
+
+void wm_manipulatormaptypes_free(void)
+{
+ for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first, *mmap_type_next;
+ mmap_type;
+ mmap_type = mmap_type_next)
+ {
+ mmap_type_next = mmap_type->next;
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_next;
+ wgt_ref;
+ wgt_ref = wgt_next)
+ {
+ wgt_next = wgt_ref->next;
+ WM_manipulatormaptype_group_free(wgt_ref);
+ }
+ MEM_freeN(mmap_type);
+ }
+}
+
+/**
+ * Initialize keymaps for all existing manipulator-groups
+ */
+void wm_manipulators_keymap(wmKeyConfig *keyconf)
+{
+ /* we add this item-less keymap once and use it to group manipulator-group keymaps into it */
+ WM_keymap_find(keyconf, "Manipulators", 0, 0);
+
+ for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) {
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) {
+ wm_manipulatorgrouptype_setup_keymap(wgt_ref->type, keyconf);
+ }
+ }
+}
+
+/** \} */ /* wmManipulatorMapType */
+
+/* -------------------------------------------------------------------- */
+/** \name Updates for Dynamic Type Registraion
+ *
+ * \{ */
+
+
+void WM_manipulatorconfig_update_tag_init(
+ wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
+{
+ /* tag for update on next use */
+ mmap_type->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT);
+ wgt->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT);
+
+ wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT;
+}
+
+void WM_manipulatorconfig_update_tag_remove(
+ wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
+{
+ /* tag for update on next use */
+ mmap_type->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
+ wgt->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
+
+ wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE;
+}
+
+/**
+ * Run incase new types have been added (runs often, early exit where possible).
+ * Follows #WM_keyconfig_update concentions.
+ */
+void WM_manipulatorconfig_update(struct Main *bmain)
+{
+ if (G.background)
+ return;
+
+ if (wm_mmap_type_update_flag == 0)
+ return;
+
+ if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) {
+ for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first;
+ mmap_type;
+ mmap_type = mmap_type->next)
+ {
+ if (mmap_type->type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) {
+ mmap_type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_ref_next;
+ wgt_ref;
+ wgt_ref = wgt_ref_next)
+ {
+ wgt_ref_next = wgt_ref->next;
+ if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_REMOVE) {
+ wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
+ WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt_ref->type);
+ }
+ }
+ }
+ }
+
+ wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE;
+ }
+
+ if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT) {
+ for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first;
+ mmap_type;
+ mmap_type = mmap_type->next)
+ {
+ const uchar type_update_all = WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
+ if (mmap_type->type_update_flag & type_update_all) {
+ mmap_type->type_update_flag &= ~type_update_all;
+ for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
+ wgt_ref;
+ wgt_ref = wgt_ref->next)
+ {
+ if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) {
+ WM_manipulatormaptype_group_init_runtime_keymap(bmain, wgt_ref->type);
+ wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
+ }
+
+ if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_INIT) {
+ WM_manipulatormaptype_group_init_runtime(bmain, mmap_type, wgt_ref->type);
+ wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_INIT;
+ }
+ }
+ }
+ }
+
+ wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT;
+ }
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c
new file mode 100644
index 00000000000..78d12e4e4ba
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c
@@ -0,0 +1,364 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmo/intern/wm_gizmo_target_props.c
+ * \ingroup wm
+ */
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+#include "BLI_string_utils.h"
+
+#include "BKE_context.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "WM_message.h"
+
+#include "wm.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+/* own includes */
+#include "wm_gizmo_wmapi.h"
+#include "wm_gizmo_intern.h"
+
+/* -------------------------------------------------------------------- */
+
+/** \name Property Definition
+ * \{ */
+
+BLI_INLINE wmManipulatorProperty *wm_manipulator_target_property_array(wmManipulator *mpr)
+{
+ return (wmManipulatorProperty *)(POINTER_OFFSET(mpr, mpr->type->struct_size));
+}
+
+wmManipulatorProperty *WM_manipulator_target_property_array(wmManipulator *mpr)
+{
+ return wm_manipulator_target_property_array(mpr);
+}
+
+wmManipulatorProperty *WM_manipulator_target_property_at_index(wmManipulator *mpr, int index)
+{
+ BLI_assert(index < mpr->type->target_property_defs_len);
+ BLI_assert(index != -1);
+ wmManipulatorProperty *mpr_prop_array = wm_manipulator_target_property_array(mpr);
+ return &mpr_prop_array[index];
+}
+
+wmManipulatorProperty *WM_manipulator_target_property_find(wmManipulator *mpr, const char *idname)
+{
+ int index = BLI_findstringindex(
+ &mpr->type->target_property_defs, idname, offsetof(wmManipulatorPropertyType, idname));
+ if (index != -1) {
+ return WM_manipulator_target_property_at_index(mpr, index);
+ }
+ else {
+ return NULL;
+ }
+}
+
+void WM_manipulator_target_property_def_rna_ptr(
+ wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type,
+ PointerRNA *ptr, PropertyRNA *prop, int index)
+{
+ wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type);
+
+ /* if manipulator evokes an operator we cannot use it for property manipulation */
+ BLI_assert(mpr->op_data == NULL);
+
+ mpr_prop->type = mpr_prop_type;
+
+ mpr_prop->ptr = *ptr;
+ mpr_prop->prop = prop;
+ mpr_prop->index = index;
+
+ if (mpr->type->property_update) {
+ mpr->type->property_update(mpr, mpr_prop);
+ }
+}
+
+void WM_manipulator_target_property_def_rna(
+ wmManipulator *mpr, const char *idname,
+ PointerRNA *ptr, const char *propname, int index)
+{
+ const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname);
+ PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
+ WM_manipulator_target_property_def_rna_ptr(mpr, mpr_prop_type, ptr, prop, index);
+}
+
+void WM_manipulator_target_property_def_func_ptr(
+ wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type,
+ const wmManipulatorPropertyFnParams *params)
+{
+ wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type);
+
+ /* if manipulator evokes an operator we cannot use it for property manipulation */
+ BLI_assert(mpr->op_data == NULL);
+
+ mpr_prop->type = mpr_prop_type;
+
+ mpr_prop->custom_func.value_get_fn = params->value_get_fn;
+ mpr_prop->custom_func.value_set_fn = params->value_set_fn;
+ mpr_prop->custom_func.range_get_fn = params->range_get_fn;
+ mpr_prop->custom_func.free_fn = params->free_fn;
+ mpr_prop->custom_func.user_data = params->user_data;
+
+ if (mpr->type->property_update) {
+ mpr->type->property_update(mpr, mpr_prop);
+ }
+}
+
+void WM_manipulator_target_property_def_func(
+ wmManipulator *mpr, const char *idname,
+ const wmManipulatorPropertyFnParams *params)
+{
+ const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname);
+ WM_manipulator_target_property_def_func_ptr(mpr, mpr_prop_type, params);
+}
+
+void WM_manipulator_target_property_clear_rna_ptr(
+ wmManipulator *mpr, const wmManipulatorPropertyType *mpr_prop_type)
+{
+ wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_at_index(mpr, mpr_prop_type->index_in_type);
+
+ /* if manipulator evokes an operator we cannot use it for property manipulation */
+ BLI_assert(mpr->op_data == NULL);
+
+ mpr_prop->type = NULL;
+
+ mpr_prop->ptr = PointerRNA_NULL;
+ mpr_prop->prop = NULL;
+ mpr_prop->index = -1;
+}
+
+void WM_manipulator_target_property_clear_rna(
+ wmManipulator *mpr, const char *idname)
+{
+ const wmManipulatorPropertyType *mpr_prop_type = WM_manipulatortype_target_property_find(mpr->type, idname);
+ WM_manipulator_target_property_clear_rna_ptr(mpr, mpr_prop_type);
+}
+
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Property Access
+ * \{ */
+
+bool WM_manipulator_target_property_is_valid_any(wmManipulator *mpr)
+{
+ wmManipulatorProperty *mpr_prop_array = wm_manipulator_target_property_array(mpr);
+ for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
+ wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
+ if (WM_manipulator_target_property_is_valid(mpr_prop)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool WM_manipulator_target_property_is_valid(const wmManipulatorProperty *mpr_prop)
+{
+ return ((mpr_prop->prop != NULL) ||
+ (mpr_prop->custom_func.value_get_fn && mpr_prop->custom_func.value_set_fn));
+}
+
+float WM_manipulator_target_property_value_get(
+ const wmManipulator *mpr, wmManipulatorProperty *mpr_prop)
+{
+ if (mpr_prop->custom_func.value_get_fn) {
+ float value = 0.0f;
+ BLI_assert(mpr_prop->type->array_length == 1);
+ mpr_prop->custom_func.value_get_fn(mpr, mpr_prop, &value);
+ return value;
+ }
+
+ if (mpr_prop->index == -1) {
+ return RNA_property_float_get(&mpr_prop->ptr, mpr_prop->prop);
+ }
+ else {
+ return RNA_property_float_get_index(&mpr_prop->ptr, mpr_prop->prop, mpr_prop->index);
+ }
+}
+
+void WM_manipulator_target_property_value_set(
+ bContext *C, const wmManipulator *mpr,
+ wmManipulatorProperty *mpr_prop, const float value)
+{
+ if (mpr_prop->custom_func.value_set_fn) {
+ BLI_assert(mpr_prop->type->array_length == 1);
+ mpr_prop->custom_func.value_set_fn(mpr, mpr_prop, &value);
+ return;
+ }
+
+ /* reset property */
+ if (mpr_prop->index == -1) {
+ RNA_property_float_set(&mpr_prop->ptr, mpr_prop->prop, value);
+ }
+ else {
+ RNA_property_float_set_index(&mpr_prop->ptr, mpr_prop->prop, mpr_prop->index, value);
+ }
+ RNA_property_update(C, &mpr_prop->ptr, mpr_prop->prop);
+}
+
+void WM_manipulator_target_property_value_get_array(
+ const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
+ float *value)
+{
+ if (mpr_prop->custom_func.value_get_fn) {
+ mpr_prop->custom_func.value_get_fn(mpr, mpr_prop, value);
+ return;
+ }
+ RNA_property_float_get_array(&mpr_prop->ptr, mpr_prop->prop, value);
+}
+
+void WM_manipulator_target_property_value_set_array(
+ bContext *C, const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
+ const float *value)
+{
+ if (mpr_prop->custom_func.value_set_fn) {
+ mpr_prop->custom_func.value_set_fn(mpr, mpr_prop, value);
+ return;
+ }
+ RNA_property_float_set_array(&mpr_prop->ptr, mpr_prop->prop, value);
+
+ RNA_property_update(C, &mpr_prop->ptr, mpr_prop->prop);
+}
+
+bool WM_manipulator_target_property_range_get(
+ const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
+ float range[2])
+{
+ if (mpr_prop->custom_func.value_get_fn) {
+ if (mpr_prop->custom_func.range_get_fn) {
+ mpr_prop->custom_func.range_get_fn(mpr, mpr_prop, range);
+ return true;
+ }
+ else {
+ return false;
+
+ }
+ }
+
+ float step, precision;
+ RNA_property_float_ui_range(&mpr_prop->ptr, mpr_prop->prop, &range[0], &range[1], &step, &precision);
+ return true;
+}
+
+int WM_manipulator_target_property_array_length(
+ const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop)
+{
+ if (mpr_prop->custom_func.value_get_fn) {
+ return mpr_prop->type->array_length;
+ }
+ return RNA_property_array_length(&mpr_prop->ptr, mpr_prop->prop);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Property Define
+ * \{ */
+
+const wmManipulatorPropertyType *WM_manipulatortype_target_property_find(
+ const wmManipulatorType *wt, const char *idname)
+{
+ return BLI_findstring(&wt->target_property_defs, idname, offsetof(wmManipulatorPropertyType, idname));
+}
+
+void WM_manipulatortype_target_property_def(
+ wmManipulatorType *wt, const char *idname, int data_type, int array_length)
+{
+ wmManipulatorPropertyType *mpt;
+
+ BLI_assert(WM_manipulatortype_target_property_find(wt, idname) == NULL);
+
+ const uint idname_size = strlen(idname) + 1;
+ mpt = MEM_callocN(sizeof(wmManipulatorPropertyType) + idname_size, __func__);
+ memcpy(mpt->idname, idname, idname_size);
+ mpt->data_type = data_type;
+ mpt->array_length = array_length;
+ mpt->index_in_type = wt->target_property_defs_len;
+ wt->target_property_defs_len += 1;
+ BLI_addtail(&wt->target_property_defs, mpt);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
+/** \name Property Utilities
+ * \{ */
+
+void WM_manipulator_do_msg_notify_tag_refresh(
+ bContext *UNUSED(C), wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val)
+{
+ ARegion *ar = msg_val->owner;
+ wmManipulatorMap *mmap = msg_val->user_data;
+
+ ED_region_tag_redraw(ar);
+ WM_manipulatormap_tag_refresh(mmap);
+}
+
+/**
+ * Runs on the "prepare draw" pass,
+ * drawing the region clears.
+ */
+void WM_manipulator_target_property_subscribe_all(
+ wmManipulator *mpr, struct wmMsgBus *mbus, ARegion *ar)
+{
+ if (mpr->type->target_property_defs_len) {
+ wmManipulatorProperty *mpr_prop_array = WM_manipulator_target_property_array(mpr);
+ for (int i = 0; i < mpr->type->target_property_defs_len; i++) {
+ wmManipulatorProperty *mpr_prop = &mpr_prop_array[i];
+ if (WM_manipulator_target_property_is_valid(mpr_prop)) {
+ if (mpr_prop->prop) {
+ WM_msg_subscribe_rna(
+ mbus, &mpr_prop->ptr, mpr_prop->prop,
+ &(const wmMsgSubscribeValue){
+ .owner = ar,
+ .user_data = ar,
+ .notify = ED_region_do_msg_notify_tag_redraw,
+ }, __func__);
+ WM_msg_subscribe_rna(
+ mbus, &mpr_prop->ptr, mpr_prop->prop,
+ &(const wmMsgSubscribeValue){
+ .owner = ar,
+ .user_data = mpr->parent_mgroup->parent_mmap,
+ .notify = WM_manipulator_do_msg_notify_tag_refresh,
+ }, __func__);
+ }
+ }
+ }
+ }
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
new file mode 100644
index 00000000000..4f31afb5b56
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_type.c
@@ -0,0 +1,212 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmo/intern/wm_gizmo_type.c
+ * \ingroup wm
+ */
+
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_string_utils.h"
+
+#include "BKE_context.h"
+#include "BKE_main.h"
+
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+
+/* only for own init/exit calls (wm_manipulatortype_init/wm_manipulatortype_free) */
+#include "wm.h"
+
+/* own includes */
+#include "wm_gizmo_wmapi.h"
+#include "wm_gizmo_intern.h"
+
+
+/** \name Manipulator Type Append
+ *
+ * \note This follows conventions from #WM_operatortype_find #WM_operatortype_append & friends.
+ * \{ */
+
+static GHash *global_manipulatortype_hash = NULL;
+
+const wmManipulatorType *WM_manipulatortype_find(const char *idname, bool quiet)
+{
+ if (idname[0]) {
+ wmManipulatorType *wt;
+
+ wt = BLI_ghash_lookup(global_manipulatortype_hash, idname);
+ if (wt) {
+ return wt;
+ }
+
+ if (!quiet) {
+ printf("search for unknown manipulator '%s'\n", idname);
+ }
+ }
+ else {
+ if (!quiet) {
+ printf("search for empty manipulator\n");
+ }
+ }
+
+ return NULL;
+}
+
+/* caller must free */
+void WM_manipulatortype_iter(GHashIterator *ghi)
+{
+ BLI_ghashIterator_init(ghi, global_manipulatortype_hash);
+}
+
+static wmManipulatorType *wm_manipulatortype_append__begin(void)
+{
+ wmManipulatorType *wt = MEM_callocN(sizeof(wmManipulatorType), "manipulatortype");
+ wt->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_ManipulatorProperties);
+#if 0
+ /* Set the default i18n context now, so that opfunc can redefine it if needed! */
+ RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
+ ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
+#endif
+ return wt;
+}
+static void wm_manipulatortype_append__end(wmManipulatorType *wt)
+{
+ BLI_assert(wt->struct_size >= sizeof(wmManipulator));
+
+ RNA_def_struct_identifier(&BLENDER_RNA, wt->srna, wt->idname);
+
+ BLI_ghash_insert(global_manipulatortype_hash, (void *)wt->idname, wt);
+}
+
+void WM_manipulatortype_append(void (*wtfunc)(struct wmManipulatorType *))
+{
+ wmManipulatorType *wt = wm_manipulatortype_append__begin();
+ wtfunc(wt);
+ wm_manipulatortype_append__end(wt);
+}
+
+void WM_manipulatortype_append_ptr(void (*wtfunc)(struct wmManipulatorType *, void *), void *userdata)
+{
+ wmManipulatorType *mt = wm_manipulatortype_append__begin();
+ wtfunc(mt, userdata);
+ wm_manipulatortype_append__end(mt);
+}
+
+/**
+ * Free but don't remove from ghash.
+ */
+static void manipulatortype_free(wmManipulatorType *wt)
+{
+ if (wt->ext.srna) { /* python manipulator, allocs own string */
+ MEM_freeN((void *)wt->idname);
+ }
+
+ BLI_freelistN(&wt->target_property_defs);
+ MEM_freeN(wt);
+}
+
+/**
+ * \param C: May be NULL.
+ */
+static void manipulatortype_unlink(
+ bContext *C, Main *bmain, wmManipulatorType *wt)
+{
+ /* Free instances. */
+ for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
+ for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
+ for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
+ ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
+ for (ARegion *ar = lb->first; ar; ar = ar->next) {
+ wmManipulatorMap *mmap = ar->manipulator_map;
+ if (mmap) {
+ wmManipulatorGroup *mgroup;
+ for (mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
+ for (wmManipulator *mpr = mgroup->manipulators.first, *mpr_next; mpr; mpr = mpr_next) {
+ mpr_next = mpr->next;
+ BLI_assert(mgroup->parent_mmap == mmap);
+ if (mpr->type == wt) {
+ WM_manipulator_unlink(&mgroup->manipulators, mgroup->parent_mmap, mpr, C);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void WM_manipulatortype_remove_ptr(bContext *C, Main *bmain, wmManipulatorType *wt)
+{
+ BLI_assert(wt == WM_manipulatortype_find(wt->idname, false));
+
+ BLI_ghash_remove(global_manipulatortype_hash, wt->idname, NULL, NULL);
+
+ manipulatortype_unlink(C, bmain, wt);
+
+ manipulatortype_free(wt);
+}
+
+bool WM_manipulatortype_remove(bContext *C, Main *bmain, const char *idname)
+{
+ wmManipulatorType *wt = BLI_ghash_lookup(global_manipulatortype_hash, idname);
+
+ if (wt == NULL) {
+ return false;
+ }
+
+ WM_manipulatortype_remove_ptr(C, bmain, wt);
+
+ return true;
+}
+
+static void wm_manipulatortype_ghash_free_cb(wmManipulatorType *mt)
+{
+ manipulatortype_free(mt);
+}
+
+void wm_manipulatortype_free(void)
+{
+ BLI_ghash_free(global_manipulatortype_hash, NULL, (GHashValFreeFP)wm_manipulatortype_ghash_free_cb);
+ global_manipulatortype_hash = NULL;
+}
+
+/* called on initialize WM_init() */
+void wm_manipulatortype_init(void)
+{
+ /* reserve size is set based on blender default setup */
+ global_manipulatortype_hash = BLI_ghash_str_new_ex("wm_manipulatortype_init gh", 128);
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/gizmo/wm_gizmo_fn.h b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
new file mode 100644
index 00000000000..8d28febbd5d
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/wm_gizmo_fn.h
@@ -0,0 +1,88 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmo/wm_gizmo_fn.h
+ * \ingroup wm
+ *
+ * Callback function definitions, needed for both Types & API headers.
+ */
+
+#ifndef __WM_GIZMO_FN_H__
+#define __WM_GIZMO_FN_H__
+
+#include "BLI_compiler_attrs.h"
+
+/* wmManipulatorGroup */
+typedef bool (*wmManipulatorGroupFnPoll)(
+ const struct bContext *, struct wmManipulatorGroupType *)
+ ATTR_WARN_UNUSED_RESULT;
+typedef void (*wmManipulatorGroupFnInit)(
+ const struct bContext *, struct wmManipulatorGroup *);
+typedef void (*wmManipulatorGroupFnRefresh)(
+ const struct bContext *, struct wmManipulatorGroup *);
+typedef void (*wmManipulatorGroupFnDrawPrepare)(
+ const struct bContext *, struct wmManipulatorGroup *);
+typedef struct wmKeyMap *(*wmManipulatorGroupFnSetupKeymap)(
+ const struct wmManipulatorGroupType *, struct wmKeyConfig *)
+ ATTR_WARN_UNUSED_RESULT;
+typedef void (*wmManipulatorGroupFnMsgBusSubscribe)(
+ const struct bContext *, struct wmManipulatorGroup *, struct wmMsgBus *);
+
+/* wmManipulator */
+/* See: wmManipulatorType for docs on each type. */
+
+typedef void (*wmManipulatorFnSetup)(struct wmManipulator *);
+typedef void (*wmManipulatorFnDraw)(const struct bContext *, struct wmManipulator *);
+typedef void (*wmManipulatorFnDrawSelect)(const struct bContext *, struct wmManipulator *, int);
+typedef int (*wmManipulatorFnTestSelect)(struct bContext *, struct wmManipulator *, const struct wmEvent *);
+typedef int (*wmManipulatorFnModal)(struct bContext *, struct wmManipulator *, const struct wmEvent *, eWM_ManipulatorTweak);
+typedef void (*wmManipulatorFnPropertyUpdate)(struct wmManipulator *, struct wmManipulatorProperty *);
+typedef void (*wmManipulatorFnMatrixBasisGet)(const struct wmManipulator *, float[4][4]);
+typedef int (*wmManipulatorFnInvoke)(struct bContext *, struct wmManipulator *, const struct wmEvent *);
+typedef void (*wmManipulatorFnExit)(struct bContext *, struct wmManipulator *, const bool);
+typedef int (*wmManipulatorFnCursorGet)(struct wmManipulator *);
+typedef void (*wmManipulatorFnSelectRefresh)(struct wmManipulator *);
+typedef void (*wmManipulatorFnFree)(struct wmManipulator *);
+
+/* wmManipulatorProperty ('value' type defined by 'wmManipulatorProperty.data_type') */
+typedef void (*wmManipulatorPropertyFnGet)(
+ const struct wmManipulator *, struct wmManipulatorProperty *,
+ /* typically 'float *' */
+ void *value);
+typedef void (*wmManipulatorPropertyFnSet)(
+ const struct wmManipulator *, struct wmManipulatorProperty *,
+ /* typically 'const float *' */
+ const void *value);
+typedef void (*wmManipulatorPropertyFnRangeGet)(
+ const struct wmManipulator *, struct wmManipulatorProperty *,
+ /* typically 'float[2]' */
+ void *range);
+typedef void (*wmManipulatorPropertyFnFree)(
+ const struct wmManipulator *, struct wmManipulatorProperty *);
+
+typedef struct wmManipulatorPropertyFnParams {
+ wmManipulatorPropertyFnGet value_get_fn;
+ wmManipulatorPropertyFnSet value_set_fn;
+ wmManipulatorPropertyFnRangeGet range_get_fn;
+ wmManipulatorPropertyFnFree free_fn;
+ void *user_data;
+} wmManipulatorPropertyFnParams;
+
+#endif /* __WM_GIZMO_FN_H__ */
diff --git a/source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h b/source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h
new file mode 100644
index 00000000000..80418cbfeb3
--- /dev/null
+++ b/source/blender/windowmanager/gizmo/wm_gizmo_wmapi.h
@@ -0,0 +1,97 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/gizmo/wm_gizmo_wmapi.h
+ * \ingroup wm
+ *
+ * \name Manipulators Window Manager API
+ * API for usage in window manager code only. It should contain all functionality
+ * needed to hook up the manipulator system with Blender's window manager. It's
+ * mostly the event system that needs to communicate with manipulator code.
+ *
+ * Only included in wm.h and lower level files.
+ */
+
+
+#ifndef __WM_GIZMO_WMAPI_H__
+#define __WM_GIZMO_WMAPI_H__
+
+struct wmEventHandler;
+struct wmManipulatorMap;
+struct wmOperatorType;
+struct wmOperator;
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+/* wm_manipulator_type.c, for init/exit */
+void wm_manipulatortype_free(void);
+void wm_manipulatortype_init(void);
+
+/* wm_manipulatorgroup_type.c, for init/exit */
+void wm_manipulatorgrouptype_free(void);
+void wm_manipulatorgrouptype_init(void);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+void MANIPULATORGROUP_OT_manipulator_select(struct wmOperatorType *ot);
+void MANIPULATORGROUP_OT_manipulator_tweak(struct wmOperatorType *ot);
+
+bool wm_manipulatorgroup_is_any_selected(const struct wmManipulatorGroup *mgroup);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+void wm_manipulatormap_remove(struct wmManipulatorMap *mmap);
+
+void wm_manipulators_keymap(struct wmKeyConfig *keyconf);
+
+void wm_manipulatormaps_handled_modal_update(
+ bContext *C, struct wmEvent *event, struct wmEventHandler *handler);
+void wm_manipulatormap_handler_context(bContext *C, struct wmEventHandler *handler);
+
+struct wmManipulator *wm_manipulatormap_highlight_find(
+ struct wmManipulatorMap *mmap, bContext *C, const struct wmEvent *event,
+ int *r_part);
+bool wm_manipulatormap_highlight_set(
+ struct wmManipulatorMap *mmap, const bContext *C,
+ struct wmManipulator *mpr, int part);
+struct wmManipulator *wm_manipulatormap_highlight_get(struct wmManipulatorMap *mmap);
+void wm_manipulatormap_modal_set(
+ struct wmManipulatorMap *mmap, bContext *C, struct wmManipulator *mpr,
+ const struct wmEvent *event, bool enable);
+
+struct wmManipulator *wm_manipulatormap_modal_get(struct wmManipulatorMap *mmap);
+struct wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len);
+struct ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMapType */
+
+void wm_manipulatormaptypes_free(void);
+
+#endif /* __WM_GIZMO_WMAPI_H__ */