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
path: root/source
diff options
context:
space:
mode:
authorJulian Eisel <eiseljulian@gmail.com>2016-10-07 17:34:55 +0300
committerJulian Eisel <eiseljulian@gmail.com>2016-10-07 17:59:55 +0300
commit6e358a1d069f9b1bd4582edae130e14106fe0698 (patch)
tree01c72efbd68209fc5e0fbe92899ea24899494415 /source
parent53d1dbbe5cfb79aa2bc904f12172fb5ab6363080 (diff)
Custom Manipulators Core Backend
This commit lands the core backend of the Custom Manipulators project onto the blender2.8 branch. It is a generic backend for managinig interactive on-screen controls that can be integrated into any 2D or 3D edito. It's also already integrated into the window-manager and editor code where needed. NOTE: The changes here should not be visible for users at all. It's really just a back-end patch. Neither does this include any RNA or Python integration. Of course, there's still lots of work ahead for custom manipulators, but this is a big milestone. WIP code that actually uses this backend can be found in the 'custom-manipulators' branch (previously called 'wiggly-widgets'). The work here isn't completely my own, all the initial work was done by @Antony Riakiotakis (psy-fi) and - although it has changed a lot since them - it's still the same in essence. He definitely deserves a big credit! Some changes in this patch were also done by @Campbell Barton (campbellbarton). Thank you guys! Merge accepted by @brecht and @merwin. Patch: https://developer.blender.org/D2232 Code documentation: https://wiki.blender.org/index.php/Dev:2.8/Source/Custom_Manipulator Main task: https://developer.blender.org/T47343 More info: https://code.blender.org/2015/09/the-custom-manipulator-project-widget-project/
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_screen.h6
-rw-r--r--source/blender/blenkernel/intern/screen.c14
-rw-r--r--source/blender/blenlib/BLI_math_vector.h1
-rw-r--r--source/blender/blenlib/intern/math_vector.c14
-rw-r--r--source/blender/blenloader/intern/readfile.c1
-rw-r--r--source/blender/editors/interface/resources.c2
-rw-r--r--source/blender/editors/screen/screen_edit.c3
-rw-r--r--source/blender/editors/space_api/spacetypes.c11
-rw-r--r--source/blender/makesdna/DNA_screen_types.h5
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h3
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h4
-rw-r--r--source/blender/windowmanager/CMakeLists.txt11
-rw-r--r--source/blender/windowmanager/WM_api.h6
-rw-r--r--source/blender/windowmanager/WM_types.h4
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c75
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c4
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c8
-rw-r--r--source/blender/windowmanager/manipulators/WM_manipulator_api.h110
-rw-r--r--source/blender/windowmanager/manipulators/WM_manipulator_types.h167
-rw-r--r--source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_intern.h100
-rw-r--r--source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_utils.c171
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator.c404
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h230
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c586
-rw-r--r--source/blender/windowmanager/manipulators/intern/wm_manipulatormap.c760
-rw-r--r--source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h90
-rw-r--r--source/blender/windowmanager/wm.h2
-rw-r--r--source/blender/windowmanager/wm_event_system.h3
-rw-r--r--source/blender/windowmanager/wm_event_types.h2
29 files changed, 2786 insertions, 11 deletions
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index 14e978b23f2..010810ad0cc 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -49,6 +49,7 @@ struct bScreen;
struct uiLayout;
struct uiList;
struct wmKeyConfig;
+struct wmManipulatorMap;
struct wmNotifier;
struct wmWindow;
struct wmWindowManager;
@@ -96,6 +97,9 @@ typedef struct SpaceType {
/* on startup, define dropboxes for spacetype+regions */
void (*dropboxes)(void);
+ /* initialize manipulator-map-types and manipulator-group-types with the region */
+ void (*manipulators)(void);
+
/* return context data */
int (*context)(const struct bContext *, const char *, struct bContextDataResult *);
@@ -284,6 +288,8 @@ void BKE_spacedata_id_unref(struct ScrArea *sa, struct SpaceLink *sl, struct ID
struct ARegion *BKE_area_region_copy(struct SpaceType *st, struct ARegion *ar);
void BKE_area_region_free(struct SpaceType *st, struct ARegion *ar);
void BKE_screen_area_free(struct ScrArea *sa);
+/* Manipulator-maps of a region need to be freed with the region. Uses callback to avoid low-level call. */
+void BKE_region_callback_free_manipulatormap_set(void (*callback)(struct wmManipulatorMap *));
struct ARegion *BKE_area_find_region_type(struct ScrArea *sa, int type);
struct ARegion *BKE_area_find_region_active_win(struct ScrArea *sa);
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 857bd5447c8..14820565478 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -180,6 +180,7 @@ ARegion *BKE_area_region_copy(SpaceType *st, ARegion *ar)
BLI_listbase_clear(&newar->panels_category_active);
BLI_listbase_clear(&newar->ui_lists);
newar->swinid = 0;
+ newar->manipulator_map = NULL;
newar->regiontimer = NULL;
/* use optional regiondata callback */
@@ -288,6 +289,17 @@ void BKE_spacedata_id_unref(struct ScrArea *sa, struct SpaceLink *sl, struct ID
}
}
+
+/**
+ * Avoid bad-level calls to #WM_manipulatormap_delete.
+ */
+static void (*region_free_manipulatormap_callback)(struct wmManipulatorMap *) = NULL;
+
+void BKE_region_callback_free_manipulatormap_set(void (*callback)(struct wmManipulatorMap *))
+{
+ region_free_manipulatormap_callback = callback;
+}
+
/* not region itself */
void BKE_area_region_free(SpaceType *st, ARegion *ar)
{
@@ -337,6 +349,8 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar)
MEM_freeN(uilst->properties);
}
}
+
+ region_free_manipulatormap_callback(ar->manipulator_map);
BLI_freelistN(&ar->ui_lists);
BLI_freelistN(&ar->ui_previews);
BLI_freelistN(&ar->panels_category);
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index e797a630948..fbecffc1270 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -304,6 +304,7 @@ void ortho_basis_v3v3_v3(float r_n1[3], float r_n2[3], const float n[3]);
void ortho_v3_v3(float out[3], const float v[3]);
void ortho_v2_v2(float out[2], const float v[2]);
void bisect_v3_v3v3v3(float r[3], const float a[3], const float b[3], const float c[3]);
+void rotate_v2_v2fl(float r[2], const float p[2], const float angle);
void rotate_v3_v3v3fl(float v[3], const float p[3], const float axis[3], const float angle);
void rotate_normalized_v3_v3v3fl(float out[3], const float p[3], const float axis[3], const float angle);
diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c
index 95d5c9fde87..90e39da5d46 100644
--- a/source/blender/blenlib/intern/math_vector.c
+++ b/source/blender/blenlib/intern/math_vector.c
@@ -772,6 +772,20 @@ void ortho_v2_v2(float out[2], const float v[2])
}
/**
+ * Rotate a point \a p by \a angle around origin (0, 0)
+ */
+void rotate_v2_v2fl(float r[2], const float p[2], const float angle)
+{
+ const float co = cosf(angle);
+ const float si = sinf(angle);
+
+ BLI_assert(r != p);
+
+ r[0] = co * p[0] - si * p[1];
+ r[1] = si * p[0] + co * p[1];
+}
+
+/**
* Rotate a point \a p by \a angle around an arbitrary unit length \a axis.
* http://local.wasp.uwa.edu.au/~pbourke/geometry/
*/
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 4e1eb41c7cf..03fcf336700 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -6516,6 +6516,7 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype)
ar->type = NULL;
ar->swap = 0;
ar->do_draw = 0;
+ ar->manipulator_map = NULL;
ar->regiontimer = NULL;
memset(&ar->drawrct, 0, sizeof(ar->drawrct));
}
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index 79fa7a7571a..2f7eb5d047c 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -1660,6 +1660,8 @@ void init_userdef_do_versions(void)
U.tw_size = 25; /* percentage of window size */
U.tw_handlesize = 16; /* percentage of widget radius */
}
+ if (U.manipulator_scale == 0)
+ U.manipulator_scale = 75;
if (U.pad_rot_angle == 0.0f)
U.pad_rot_angle = 15.0f;
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 677a6472c72..3d30f2be445 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -1069,6 +1069,9 @@ static void region_cursor_set(wmWindow *win, int swinid, int swin_changed)
for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->swinid == swinid) {
if (swin_changed || (ar->type && ar->type->event_cursor)) {
+ if (WM_manipulatormap_cursor_set(ar->manipulator_map, win)) {
+ return;
+ }
ED_region_cursor_set(win, sa, ar);
}
return;
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index ac6e3123e4e..5ff1d758563 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -121,12 +121,17 @@ void ED_spacetypes_init(void)
ED_operatortypes_view2d();
ED_operatortypes_ui();
-
- /* register operators */
+
+ /* register types for operators and manipulators */
spacetypes = BKE_spacetypes_list();
for (type = spacetypes->first; type; type = type->next) {
- if (type->operatortypes)
+ /* init manipulator types first, operator-types need them */
+ if (type->manipulators) {
+ type->manipulators();
+ }
+ if (type->operatortypes) {
type->operatortypes();
+ }
}
/* register internal render callbacks */
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index e208ef39719..2efb9d1f1ac 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -265,9 +265,10 @@ typedef struct ARegion {
ListBase ui_previews; /* uiPreview */
ListBase handlers; /* wmEventHandler */
ListBase panels_category; /* Panel categories runtime */
-
+
+ struct wmManipulatorMap *manipulator_map; /* manipulator-map of this region */
struct wmTimer *regiontimer; /* blend in/out */
-
+
char *headerstr; /* use this string to draw info */
void *regiondata; /* XXX 2.50, need spacedata equivalent? */
} ARegion;
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 759ebbfb5cb..30d81df07ef 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -490,6 +490,7 @@ typedef struct UserDef {
short tb_leftmouse, tb_rightmouse;
struct SolidLight light[3];
short tw_hotspot, tw_flag, tw_handlesize, tw_size;
+ short manipulator_scale, pad3[3];
short textimeout, texcollectrate;
short wmdrawmethod; /* removed wmpad */
short dragthreshold;
@@ -782,8 +783,6 @@ typedef enum eText_Draw_Options {
USER_TEXT_DISABLE_AA = (1 << 0),
} eText_Draw_Options;
-/* tw_flag (transform widget) */
-
/* gp_settings (Grease Pencil Settings) */
typedef enum eGP_UserdefSettings {
GP_PAINT_DOSMOOTH = (1 << 0),
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index a2b470e062b..e2d156ee3ca 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -111,7 +111,7 @@ typedef struct RegionView3D {
struct wmTimer *smooth_timer;
- /* transform widget matrix */
+ /* transform manipulator matrix */
float twmat[4][4];
float viewquat[4]; /* view rotation, must be kept normalized */
@@ -202,7 +202,7 @@ typedef struct View3D {
short gridsubdiv; /* Number of subdivisions in the grid between each highlighted grid line */
char gridflag;
- /* transform widget info */
+ /* transform manipulator info */
char twtype, twmode, twflag;
short flag3;
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index b6245a8c0d1..b5c19534b15 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -25,6 +25,8 @@
set(INC
.
+ manipulators
+ manipulators/intern
../blenfont
../blenkernel
../blenlib
@@ -68,6 +70,10 @@ set(SRC
intern/wm_subwindow.c
intern/wm_window.c
intern/wm_stereo.c
+ manipulators/intern/wm_manipulator.c
+ manipulators/intern/wm_manipulatorgroup.c
+ manipulators/intern/wm_manipulatormap.c
+ manipulators/intern/manipulator_library/manipulator_library_utils.c
WM_api.h
WM_keymap.h
@@ -80,6 +86,11 @@ set(SRC
wm_files.h
wm_subwindow.h
wm_window.h
+ manipulators/WM_manipulator_api.h
+ manipulators/WM_manipulator_types.h
+ manipulators/wm_manipulator_wmapi.h
+ manipulators/intern/wm_manipulator_intern.h
+ manipulators/intern/manipulator_library/manipulator_library_intern.h
)
if(WITH_AUDASPACE)
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 2b82f1becb3..4d159c69053 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -42,6 +42,9 @@
#include "WM_keymap.h"
#include "BLI_compiler_attrs.h"
+/* Include external manipulator API's */
+#include "manipulators/WM_manipulator_api.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -70,6 +73,9 @@ struct wmNDOFMotionData;
#endif
typedef struct wmJob wmJob;
+typedef struct wmManipulator wmManipulator;
+typedef struct wmManipulatorMap wmManipulatorMap;
+typedef struct wmManipulatorMapType wmManipulatorMapType;
/* general API */
void WM_init_state_size_set (int stax, int stay, int sizx, int sizy);
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index ce4a69a1841..547968e2906 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -119,6 +119,7 @@ struct ImBuf;
/* exported types for WM */
#include "wm_cursors.h"
#include "wm_event_types.h"
+#include "manipulators/WM_manipulator_types.h"
/* ************** wmOperatorType ************************ */
@@ -566,6 +567,9 @@ typedef struct wmOperatorType {
/* pointer to modal keymap, do not free! */
struct wmKeyMap *modalkeymap;
+ /* manipulator-group that is accessible while operator runs */
+ wmManipulatorGroupType *mgrouptype;
+
/* python needs the operator type as well */
int (*pyop_poll)(struct bContext *, struct wmOperatorType *ot) ATTR_WARN_UNUSED_RESULT;
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 7625f55be6e..a75b3b1a0e6 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -1672,7 +1672,12 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
wm_handler_op_context(C, handler, event);
wm_region_mouse_co(C, event);
wm_event_modalkeymap(C, op, event, &dbl_click_disabled);
-
+
+ /* attach manipulator-map to handler if not there yet */
+ if (ot->mgrouptype && !handler->manipulator_map) {
+ wm_manipulatorgroup_attach_to_modal_handler(C, handler, ot->mgrouptype, op);
+ }
+
if (ot->flag & OPTYPE_UNDO)
wm->op_undo_depth++;
@@ -1721,6 +1726,9 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
CTX_wm_region_set(C, NULL);
}
+ /* update manipulators during modal handlers */
+ wm_manipulatormaps_handled_modal_update(C, event, handler, ot);
+
/* remove modal handler, operator itself should have been canceled and freed */
if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
WM_cursor_grab_disable(CTX_wm_window(C), NULL);
@@ -2090,6 +2098,71 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
}
}
}
+ else if (handler->manipulator_map) {
+ ScrArea *area = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
+ wmManipulatorMap *mmap = handler->manipulator_map;
+ wmManipulator *manipulator = wm_manipulatormap_get_highlighted_manipulator(mmap);
+ unsigned char part;
+
+ wm_manipulatormap_handler_context(C, handler);
+ wm_region_mouse_co(C, event);
+
+ /* handle manipulator highlighting */
+ if (event->type == MOUSEMOVE && !wm_manipulatormap_get_active_manipulator(mmap)) {
+ manipulator = wm_manipulatormap_find_highlighted_manipulator(mmap, C, event, &part);
+ wm_manipulatormap_set_highlighted_manipulator(mmap, C, manipulator, part);
+ }
+ /* handle user configurable manipulator-map keymap */
+ else if (manipulator) {
+ /* get user customized keymap from default one */
+ const wmManipulatorGroup *highlightgroup = wm_manipulator_get_parent_group(manipulator);
+ const wmKeyMap *keymap = WM_keymap_active(wm, highlightgroup->type->keymap);
+ wmKeyMapItem *kmi;
+
+ PRINT("%s: checking '%s' ...", __func__, keymap->idname);
+
+ if (!keymap->poll || keymap->poll(C)) {
+ PRINT("pass\n");
+ for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
+ if (wm_eventmatch(event, kmi)) {
+ wmOperator *op = handler->op;
+
+ PRINT("%s: item matched '%s'\n", __func__, kmi->idname);
+
+ /* weak, but allows interactive callback to not use rawkey */
+ event->keymap_idname = kmi->idname;
+
+ /* handler->op is called later, we want keymap op to be triggered here */
+ handler->op = NULL;
+ action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
+ handler->op = op;
+
+ if (action & WM_HANDLER_BREAK) {
+ if (action & WM_HANDLER_HANDLED) {
+ if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS))
+ printf("%s: handled - and pass on! '%s'\n", __func__, kmi->idname);
+ }
+ else {
+ PRINT("%s: un-handled '%s'\n", __func__, kmi->idname);
+ }
+ }
+ }
+ }
+ }
+ else {
+ PRINT("fail\n");
+ }
+ }
+
+ /* restore the area */
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+
+ if (handler->op) {
+ action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
+ }
+ }
else {
/* modal, swallows all */
action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index c11c398c616..fbbde2a6d28 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -169,6 +169,7 @@ void WM_init(bContext *C, int argc, const char **argv)
BKE_library_callback_free_window_manager_set(wm_close_and_free); /* library.c */
BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference); /* library.c */
+ BKE_region_callback_free_manipulatormap_set(wm_manipulatormap_delete); /* screen.c */
BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference); /* library.c */
BKE_blender_callback_test_break_set(wm_window_testbreak); /* blender.c */
BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); /* screen.c */
@@ -529,6 +530,9 @@ void WM_exit_ext(bContext *C, const bool do_python)
ED_gpencil_strokes_copybuf_free();
BKE_node_clipboard_clear();
+ /* free manipulator-maps after freeing blender, so no deleted data get accessed during cleaning up of areas */
+ wm_manipulatormaptypes_free();
+
BLF_exit();
#ifdef WITH_INTERNATIONAL
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 87ef0596e52..33f12927f2b 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -491,6 +491,9 @@ void WM_operatortype_remove_ptr(wmOperatorType *ot)
BLI_ghash_remove(global_ops_hash, ot->idname, NULL, NULL);
WM_keyconfig_update_operatortype();
+ if (ot->mgrouptype) {
+ WM_manipulatorgrouptype_unregister(NULL, G.main, ot->mgrouptype);
+ }
MEM_freeN(ot);
}
@@ -4170,6 +4173,10 @@ void wm_operatortype_init(void)
WM_operatortype_append(WM_OT_previews_ensure);
WM_operatortype_append(WM_OT_previews_clear);
WM_operatortype_append(WM_OT_doc_view_manual_ui_context);
+
+ /* manipulators */
+ WM_operatortype_append(MANIPULATORGROUP_OT_manipulator_select);
+ WM_operatortype_append(MANIPULATORGROUP_OT_manipulator_tweak);
}
/* circleselect-like modal operators */
@@ -4475,6 +4482,7 @@ void wm_window_keymap(wmKeyConfig *keyconf)
RNA_float_set(kmi->ptr, "value", 1.0f / 1.5f);
#endif /* WITH_INPUT_NDOF */
+ wm_manipulators_keymap(keyconf);
gesture_circle_modal_keymap(keyconf);
gesture_border_modal_keymap(keyconf);
gesture_zoom_border_modal_keymap(keyconf);
diff --git a/source/blender/windowmanager/manipulators/WM_manipulator_api.h b/source/blender/windowmanager/manipulators/WM_manipulator_api.h
new file mode 100644
index 00000000000..099ad7fd851
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/WM_manipulator_api.h
@@ -0,0 +1,110 @@
+/*
+ * ***** 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/manipulators/WM_manipulator_api.h
+ * \ingroup wm
+ *
+ * \name Manipulator API
+ * \brief API for external use of wmManipulator types.
+ *
+ * Only included in WM_api.h
+ */
+
+
+#ifndef __WM_MANIPULATOR_API_H__
+#define __WM_MANIPULATOR_API_H__
+
+struct ARegion;
+struct Main;
+struct wmKeyConfig;
+struct wmManipulatorGroupType;
+struct wmManipulatorMap;
+struct wmManipulatorMapType;
+struct wmManipulatorMapType_Params;
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+struct wmManipulator *WM_manipulator_new(
+ void (*draw)(const struct bContext *, struct wmManipulator *),
+ void (*render_3d_intersection)(const struct bContext *, struct wmManipulator *, int),
+ int (*intersect)(struct bContext *, const struct wmEvent *, struct wmManipulator *),
+ int (*handler)(struct bContext *, const struct wmEvent *, struct wmManipulator *, const int));
+void WM_manipulator_delete(
+ ListBase *manipulatorlist, struct wmManipulatorMap *mmap, struct wmManipulator *manipulator,
+ struct bContext *C);
+
+void WM_manipulator_set_property(struct wmManipulator *, int slot, struct PointerRNA *ptr, const char *propname);
+struct PointerRNA *WM_manipulator_set_operator(struct wmManipulator *, const char *opname);
+void WM_manipulator_set_func_select(
+ struct wmManipulator *manipulator,
+ void (*select)(struct bContext *, struct wmManipulator *, const int action)); /* wmManipulatorSelectFunc */
+void WM_manipulator_set_origin(struct wmManipulator *manipulator, const float origin[3]);
+void WM_manipulator_set_offset(struct wmManipulator *manipulator, const float offset[3]);
+void WM_manipulator_set_flag(struct wmManipulator *manipulator, const int flag, const bool enable);
+void WM_manipulator_set_scale(struct wmManipulator *manipulator, float scale);
+void WM_manipulator_set_line_width(struct wmManipulator *manipulator, const float line_width);
+void WM_manipulator_set_colors(struct wmManipulator *manipulator, const float col[4], const float col_hi[4]);
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+struct wmManipulatorGroupType *WM_manipulatorgrouptype_append(
+ struct wmManipulatorMapType *mmaptype,
+ void (*mgrouptype_func)(struct wmManipulatorGroupType *));
+struct wmManipulatorGroupType *WM_manipulatorgrouptype_append_runtime(
+ const struct Main *main, struct wmManipulatorMapType *mmaptype,
+ void (*mgrouptype_func)(struct wmManipulatorGroupType *));
+void WM_manipulatorgrouptype_init_runtime(
+ const struct Main *bmain, struct wmManipulatorMapType *mmaptype,
+ struct wmManipulatorGroupType *mgrouptype);
+void WM_manipulatorgrouptype_unregister(
+ struct bContext *C, struct Main *bmain, struct wmManipulatorGroupType *mgroup);
+
+struct wmKeyMap *WM_manipulatorgroup_keymap_common(
+ const struct wmManipulatorGroupType *mgrouptype, struct wmKeyConfig *config);
+struct wmKeyMap *WM_manipulatorgroup_keymap_common_sel(
+ const struct wmManipulatorGroupType *mgrouptype, struct wmKeyConfig *config);
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+struct wmManipulatorMapType *WM_manipulatormaptype_find(
+ const struct wmManipulatorMapType_Params *mmap_params);
+struct wmManipulatorMapType *WM_manipulatormaptype_ensure(
+ const struct wmManipulatorMapType_Params *mmap_params);
+
+struct wmManipulatorMap *WM_manipulatormap_new_from_type(
+ const struct wmManipulatorMapType_Params *mmap_params);
+void WM_manipulatormap_tag_refresh(struct wmManipulatorMap *mmap);
+void WM_manipulatormap_draw(struct wmManipulatorMap *mmap, const struct bContext *C, const int 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);
+
+#endif /* __WM_MANIPULATOR_API_H__ */
+
diff --git a/source/blender/windowmanager/manipulators/WM_manipulator_types.h b/source/blender/windowmanager/manipulators/WM_manipulator_types.h
new file mode 100644
index 00000000000..284a3e9bd22
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/WM_manipulator_types.h
@@ -0,0 +1,167 @@
+/*
+ * ***** 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/manipulators/WM_manipulator_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_MANIPULATOR_TYPES_H__
+#define __WM_MANIPULATOR_TYPES_H__
+
+#include "BLI_compiler_attrs.h"
+
+struct wmManipulatorGroupType;
+struct wmManipulatorGroup;
+struct wmKeyConfig;
+
+typedef bool (*wmManipulatorGroupPollFunc)(const struct bContext *, struct wmManipulatorGroupType *) ATTR_WARN_UNUSED_RESULT;
+typedef void (*wmManipulatorGroupInitFunc)(const struct bContext *, struct wmManipulatorGroup *);
+typedef void (*wmManipulatorGroupRefreshFunc)(const struct bContext *, struct wmManipulatorGroup *);
+typedef void (*wmManipulatorGroupDrawPrepareFunc)(const struct bContext *, struct wmManipulatorGroup *);
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+/**
+ * Simple utility wrapper for storing a single manipulator as wmManipulatorGroup.customdata (which gets freed).
+ */
+typedef struct wmManipulatorWrapper {
+ struct wmManipulator *manipulator;
+} wmManipulatorWrapper;
+
+/* wmManipulator.flag
+ * Flags for individual manipulators. */
+enum {
+ WM_MANIPULATOR_DRAW_HOVER = (1 << 0), /* draw *only* while hovering */
+ WM_MANIPULATOR_DRAW_ACTIVE = (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),
+};
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+typedef struct wmManipulatorGroup {
+ struct wmManipulatorGroup *next, *prev;
+
+ struct wmManipulatorGroupType *type;
+ ListBase manipulators;
+
+ 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 */
+ int flag; /* private */
+ int pad;
+} wmManipulatorGroup;
+
+/* factory class for a manipulator-group type, gets called every time a new area is spawned */
+typedef struct wmManipulatorGroupType {
+ struct wmManipulatorGroupType *next, *prev;
+
+ char idname[64]; /* MAX_NAME */
+ const char *name; /* manipulator-group name - displayed in UI (keymap editor) */
+
+ /* poll if manipulator-map should be visible */
+ wmManipulatorGroupPollFunc poll;
+ /* initially create manipulators and set permanent data - stuff you only need to do once */
+ wmManipulatorGroupInitFunc init;
+ /* refresh data, only called if recreate flag is set (WM_manipulatormap_tag_refresh) */
+ wmManipulatorGroupRefreshFunc refresh;
+ /* refresh data for drawing, called before each redraw */
+ wmManipulatorGroupDrawPrepareFunc draw_prepare;
+
+ /* keymap init callback for this manipulator-group */
+ struct wmKeyMap *(*keymap_init)(const struct wmManipulatorGroupType *, struct wmKeyConfig *);
+ /* keymap created with callback from above */
+ struct wmKeyMap *keymap;
+
+ /* rna for properties */
+ struct StructRNA *srna;
+
+ /* RNA integration */
+ ExtensionRNA ext;
+
+ int flag;
+
+ /* if type is spawned from operator this is set here */
+ void *op;
+
+ /* same as manipulator-maps, so registering/unregistering goes to the correct region */
+ short spaceid, regionid;
+ char mapidname[64];
+} wmManipulatorGroupType;
+
+/**
+ * wmManipulatorGroupType.flag
+ * Flags that influence the behavior of all manipulators in the group.
+ */
+enum {
+ /* Mark manipulator-group as being 3D */
+ WM_MANIPULATORGROUPTYPE_IS_3D = (1 << 0),
+ /* Scale manipulators as 3D object that respects zoom (otherwise zoom independent draw size) */
+ WM_MANIPULATORGROUPTYPE_SCALE_3D = (1 << 1),
+ /* Manipulators can be depth culled with scene objects (covered by other geometry - TODO) */
+ WM_MANIPULATORGROUPTYPE_SCENE_DEPTH = (1 << 2),
+ /* Manipulators can be selected */
+ WM_MANIPULATORGROUPTYPE_SELECTABLE = (1 << 3),
+ /* manipulator group is attached to operator, and is only accessible as long as this runs */
+ WM_MANIPULATORGROUPTYPE_OP = (1 << 4),
+};
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+struct wmManipulatorMapType_Params {
+ const char *idname;
+ const int spaceid;
+ const int regionid;
+};
+
+/**
+ * Pass a value of this enum to #WM_manipulatormap_draw to tell it what to draw.
+ */
+enum {
+ /* Draw 2D manipulator-groups (ManipulatorGroupType.is_3d == false) */
+ WM_MANIPULATORMAP_DRAWSTEP_2D = 0,
+ /* Draw 3D manipulator-groups (ManipulatorGroupType.is_3d == true) */
+ WM_MANIPULATORMAP_DRAWSTEP_3D,
+ /* Draw only depth culled manipulators (WM_MANIPULATOR_SCENE_DEPTH flag).
+ * Note that these are expected to be 3D manipulators too. */
+ WM_MANIPULATORMAP_DRAWSTEP_IN_SCENE,
+};
+
+#endif /* __WM_MANIPULATOR_TYPES_H__ */
+
diff --git a/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_intern.h b/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_intern.h
new file mode 100644
index 00000000000..66598fa29d7
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_intern.h
@@ -0,0 +1,100 @@
+/*
+ * ***** 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/manipulators/intern/manipulator_library/manipulator_library_intern.h
+ * \ingroup wm
+ */
+
+
+#ifndef __MANIPULATOR_LIBRARY_INTERN_H__
+#define __MANIPULATOR_LIBRARY_INTERN_H__
+
+/* distance around which manipulators respond to input (and get highlighted) */
+#define MANIPULATOR_HOTSPOT 14.0f
+
+/**
+ * Data for common interactions. Used in manipulator_library_utils.c functions.
+ */
+typedef struct ManipulatorCommonData {
+ int flag;
+
+ float range_fac; /* factor for arrow min/max distance */
+ float offset;
+
+ /* property range for constrained manipulators */
+ float range;
+ /* min/max value for constrained manipulators */
+ float min, max;
+} ManipulatorCommonData;
+
+typedef struct ManipulatorInteraction {
+ float init_value; /* initial property value */
+ float init_origin[3];
+ float init_mval[2];
+ float init_offset;
+ float init_scale;
+
+ /* offset of last handling step */
+ float prev_offset;
+ /* Total offset added by precision tweaking.
+ * Needed to allow toggling precision on/off without causing jumps */
+ float precision_offset;
+} ManipulatorInteraction;
+
+/* ManipulatorCommonData->flag */
+enum {
+ MANIPULATOR_CUSTOM_RANGE_SET = (1 << 0),
+};
+
+
+float manipulator_offset_from_value(
+ ManipulatorCommonData *data, const float value,
+ const bool constrained, const bool inverted);
+float manipulator_value_from_offset(
+ ManipulatorCommonData *data, ManipulatorInteraction *inter, const float offset,
+ const bool constrained, const bool inverted, const bool use_precision);
+
+void manipulator_property_data_update(
+ wmManipulator *manipulator, ManipulatorCommonData *data, const int slot,
+ const bool constrained, const bool inverted);
+
+void manipulator_property_value_set(
+ bContext *C, const wmManipulator *manipulator,
+ const int slot, const float value);
+float manipulator_property_value_get(
+ const wmManipulator *manipulator, const int slot);
+void manipulator_property_value_reset(
+ bContext *C, const wmManipulator *manipulator, ManipulatorInteraction *inter,
+ const int slot);
+
+
+/* -------------------------------------------------------------------- */
+
+void manipulator_color_get(
+ const wmManipulator *manipulator, const bool highlight,
+ float r_col[]);
+
+#endif /* __MANIPULATOR_LIBRARY_INTERN_H__ */
+
diff --git a/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_utils.c b/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_utils.c
new file mode 100644
index 00000000000..0617e9e873b
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_utils.c
@@ -0,0 +1,171 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/manipulators/intern/manipulator_library/manipulator_library_utils.c
+ * \ingroup wm
+ *
+ * \name Manipulator Library Utilities
+ *
+ * \brief This file contains functions for common behaviors of manipulators.
+ */
+
+#include "BKE_context.h"
+
+#include "BLI_math.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+
+/* own includes */
+#include "WM_manipulator_types.h"
+#include "wm_manipulator_wmapi.h"
+#include "wm_manipulator_intern.h"
+#include "manipulator_library_intern.h"
+
+/* factor for precision tweaking */
+#define MANIPULATOR_PRECISION_FAC 0.05f
+
+
+BLI_INLINE float manipulator_offset_from_value_constr(
+ const float range_fac, const float min, const float range, const float value,
+ const bool inverted)
+{
+ return inverted ? (range_fac * (min + range - value) / range) : (range_fac * (value / range));
+}
+
+BLI_INLINE float manipulator_value_from_offset_constr(
+ const float range_fac, const float min, const float range, const float value,
+ const bool inverted)
+{
+ return inverted ? (min + range - (value * range / range_fac)) : (value * range / range_fac);
+}
+
+float manipulator_offset_from_value(
+ ManipulatorCommonData *data, const float value, const bool constrained, const bool inverted)
+{
+ if (constrained)
+ return manipulator_offset_from_value_constr(data->range_fac, data->min, data->range, value, inverted);
+
+ return value;
+}
+
+float manipulator_value_from_offset(
+ ManipulatorCommonData *data, ManipulatorInteraction *inter, const float offset,
+ const bool constrained, const bool inverted, const bool use_precision)
+{
+ const float max = data->min + data->range;
+
+ if (use_precision) {
+ /* add delta offset of this step to total precision_offset */
+ inter->precision_offset += offset - inter->prev_offset;
+ }
+ inter->prev_offset = offset;
+
+ float ofs_new = inter->init_offset + offset - inter->precision_offset * (1.0f - MANIPULATOR_PRECISION_FAC);
+ float value;
+
+ if (constrained) {
+ value = manipulator_value_from_offset_constr(data->range_fac, data->min, data->range, ofs_new, inverted);
+ }
+ else {
+ value = ofs_new;
+ }
+
+ /* clamp to custom range */
+ if (data->flag & MANIPULATOR_CUSTOM_RANGE_SET) {
+ CLAMP(value, data->min, max);
+ }
+
+ return value;
+}
+
+void manipulator_property_data_update(
+ wmManipulator *manipulator, ManipulatorCommonData *data, const int slot,
+ const bool constrained, const bool inverted)
+{
+ if (!manipulator->props[slot]) {
+ data->offset = 0.0f;
+ return;
+ }
+
+ PointerRNA ptr = manipulator->ptr[slot];
+ PropertyRNA *prop = manipulator->props[slot];
+ float value = manipulator_property_value_get(manipulator, slot);
+
+ if (constrained) {
+ if ((data->flag & MANIPULATOR_CUSTOM_RANGE_SET) == 0) {
+ float step, precision;
+ float min, max;
+ RNA_property_float_ui_range(&ptr, prop, &min, &max, &step, &precision);
+ data->range = max - min;
+ data->min = min;
+ }
+ data->offset = manipulator_offset_from_value_constr(data->range_fac, data->min, data->range, value, inverted);
+ }
+ else {
+ data->offset = value;
+ }
+}
+
+void manipulator_property_value_set(
+ bContext *C, const wmManipulator *manipulator,
+ const int slot, const float value)
+{
+ PointerRNA ptr = manipulator->ptr[slot];
+ PropertyRNA *prop = manipulator->props[slot];
+
+ /* reset property */
+ RNA_property_float_set(&ptr, prop, value);
+ RNA_property_update(C, &ptr, prop);
+}
+
+float manipulator_property_value_get(const wmManipulator *manipulator, const int slot)
+{
+ BLI_assert(RNA_property_type(manipulator->props[slot]) == PROP_FLOAT);
+ return RNA_property_float_get(&manipulator->ptr[slot], manipulator->props[slot]);
+}
+
+void manipulator_property_value_reset(
+ bContext *C, const wmManipulator *manipulator, ManipulatorInteraction *inter,
+ const int slot)
+{
+ manipulator_property_value_set(C, manipulator, slot, inter->init_value);
+}
+
+
+/* -------------------------------------------------------------------- */
+
+void manipulator_color_get(
+ const wmManipulator *manipulator, const bool highlight,
+ float r_col[4])
+{
+ if (highlight && !(manipulator->flag & WM_MANIPULATOR_DRAW_HOVER)) {
+ copy_v4_v4(r_col, manipulator->col_hi);
+ }
+ else {
+ copy_v4_v4(r_col, manipulator->col);
+ }
+}
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator.c b/source/blender/windowmanager/manipulators/intern/wm_manipulator.c
new file mode 100644
index 00000000000..824141d1c40
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator.c
@@ -0,0 +1,404 @@
+/*
+ * ***** 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/manipulators/intern/wm_manipulator.c
+ * \ingroup wm
+ */
+
+#include "BKE_context.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "GL/glew.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+/* own includes */
+#include "wm_manipulator_wmapi.h"
+#include "wm_manipulator_intern.h"
+
+
+/* Still unused */
+wmManipulator *WM_manipulator_new(
+ void (*draw)(const bContext *C, wmManipulator *customdata),
+ void (*render_3d_intersection)(const bContext *C, wmManipulator *customdata, int selectionbase),
+ int (*intersect)(bContext *C, const wmEvent *event, wmManipulator *manipulator),
+ int (*handler)(bContext *C, const wmEvent *event, wmManipulator *manipulator, const int flag))
+{
+ wmManipulator *manipulator = MEM_callocN(sizeof(wmManipulator), "manipulator");
+
+ manipulator->draw = draw;
+ manipulator->handler = handler;
+ manipulator->intersect = intersect;
+ manipulator->render_3d_intersection = render_3d_intersection;
+
+ /* XXX */
+// fix_linking_manipulator_arrow();
+// fix_linking_manipulator_arrow2d();
+// fix_linking_manipulator_cage();
+// fix_linking_manipulator_dial();
+// fix_linking_manipulator_facemap();
+// fix_linking_manipulator_primitive();
+
+ return manipulator;
+}
+
+/**
+ * Assign an idname that is unique in \a mgroup to \a manipulator.
+ *
+ * \param rawname: Name used as basis to define final unique idname.
+ */
+static void manipulator_unique_idname_set(wmManipulatorGroup *mgroup, wmManipulator *manipulator, const char *rawname)
+{
+ if (mgroup->type->idname[0]) {
+ BLI_snprintf(manipulator->idname, sizeof(manipulator->idname), "%s_%s", mgroup->type->idname, rawname);
+ }
+ else {
+ BLI_strncpy(manipulator->idname, rawname, sizeof(manipulator->idname));
+ }
+
+ /* ensure name is unique, append '.001', '.002', etc if not */
+ BLI_uniquename(&mgroup->manipulators, manipulator, "Manipulator", '.',
+ offsetof(wmManipulator, idname), sizeof(manipulator->idname));
+}
+
+/**
+ * Initialize default values and allocate needed memory for members.
+ */
+static void manipulator_init(wmManipulator *manipulator)
+{
+ const float col_default[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+
+ manipulator->user_scale = 1.0f;
+ manipulator->line_width = 1.0f;
+
+ /* defaults */
+ copy_v4_v4(manipulator->col, col_default);
+ copy_v4_v4(manipulator->col_hi, col_default);
+
+ /* create at least one property for interaction */
+ if (manipulator->max_prop == 0) {
+ manipulator->max_prop = 1;
+ }
+
+ manipulator->props = MEM_callocN(sizeof(PropertyRNA *) * manipulator->max_prop, "manipulator->props");
+ manipulator->ptr = MEM_callocN(sizeof(PointerRNA) * manipulator->max_prop, "manipulator->ptr");
+}
+
+/**
+ * Register \a manipulator.
+ *
+ * \param name: name used to create a unique idname for \a manipulator in \a mgroup
+ */
+void wm_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *manipulator, const char *name)
+{
+ manipulator_init(manipulator);
+ manipulator_unique_idname_set(mgroup, manipulator, name);
+ wm_manipulatorgroup_manipulator_register(mgroup, manipulator);
+}
+
+/**
+ * Free \a manipulator and unlink from \a manipulatorlist.
+ * \a manipulatorlist is allowed to be NULL.
+ */
+void WM_manipulator_delete(ListBase *manipulatorlist, wmManipulatorMap *mmap, wmManipulator *manipulator, bContext *C)
+{
+ if (manipulator->state & WM_MANIPULATOR_HIGHLIGHT) {
+ wm_manipulatormap_set_highlighted_manipulator(mmap, C, NULL, 0);
+ }
+ if (manipulator->state & WM_MANIPULATOR_ACTIVE) {
+ wm_manipulatormap_set_active_manipulator(mmap, C, NULL, NULL);
+ }
+ if (manipulator->state & WM_MANIPULATOR_SELECTED) {
+ wm_manipulator_deselect(mmap, manipulator);
+ }
+
+ if (manipulator->opptr.data) {
+ WM_operator_properties_free(&manipulator->opptr);
+ }
+ MEM_freeN(manipulator->props);
+ MEM_freeN(manipulator->ptr);
+
+ if (manipulatorlist)
+ BLI_remlink(manipulatorlist, manipulator);
+ MEM_freeN(manipulator);
+}
+
+wmManipulatorGroup *wm_manipulator_get_parent_group(const wmManipulator *manipulator)
+{
+ return manipulator->mgroup;
+}
+
+
+/* -------------------------------------------------------------------- */
+/** \name Manipulator Creation API
+ *
+ * API for defining data on manipulator creation.
+ *
+ * \{ */
+
+void WM_manipulator_set_property(wmManipulator *manipulator, const int slot, PointerRNA *ptr, const char *propname)
+{
+ if (slot < 0 || slot >= manipulator->max_prop) {
+ fprintf(stderr, "invalid index %d when binding property for manipulator type %s\n", slot, manipulator->idname);
+ return;
+ }
+
+ /* if manipulator evokes an operator we cannot use it for property manipulation */
+ manipulator->opname = NULL;
+ manipulator->ptr[slot] = *ptr;
+ manipulator->props[slot] = RNA_struct_find_property(ptr, propname);
+
+ if (manipulator->prop_data_update)
+ manipulator->prop_data_update(manipulator, slot);
+}
+
+PointerRNA *WM_manipulator_set_operator(wmManipulator *manipulator, const char *opname)
+{
+ wmOperatorType *ot = WM_operatortype_find(opname, 0);
+
+ if (ot) {
+ manipulator->opname = opname;
+
+ if (manipulator->opptr.data) {
+ WM_operator_properties_free(&manipulator->opptr);
+ }
+ WM_operator_properties_create_ptr(&manipulator->opptr, ot);
+
+ return &manipulator->opptr;
+ }
+ else {
+ fprintf(stderr, "Error binding operator to manipulator: operator %s not found!\n", opname);
+ }
+
+ return NULL;
+}
+
+/**
+ * \brief Set manipulator select callback.
+ *
+ * Callback is called when manipulator gets selected/deselected.
+ */
+void WM_manipulator_set_func_select(wmManipulator *manipulator, wmManipulatorSelectFunc select)
+{
+ BLI_assert(manipulator->mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECTABLE);
+ manipulator->select = select;
+}
+
+void WM_manipulator_set_origin(wmManipulator *manipulator, const float origin[3])
+{
+ copy_v3_v3(manipulator->origin, origin);
+}
+
+void WM_manipulator_set_offset(wmManipulator *manipulator, const float offset[3])
+{
+ copy_v3_v3(manipulator->offset, offset);
+}
+
+void WM_manipulator_set_flag(wmManipulator *manipulator, const int flag, const bool enable)
+{
+ if (enable) {
+ manipulator->flag |= flag;
+ }
+ else {
+ manipulator->flag &= ~flag;
+ }
+}
+
+void WM_manipulator_set_scale(wmManipulator *manipulator, const float scale)
+{
+ manipulator->user_scale = scale;
+}
+
+void WM_manipulator_set_line_width(wmManipulator *manipulator, const float line_width)
+{
+ manipulator->line_width = line_width;
+}
+
+/**
+ * Set manipulator rgba colors.
+ *
+ * \param col Normal state color.
+ * \param col_hi Highlighted state color.
+ */
+void WM_manipulator_set_colors(wmManipulator *manipulator, const float col[4], const float col_hi[4])
+{
+ copy_v4_v4(manipulator->col, col);
+ copy_v4_v4(manipulator->col_hi, col_hi);
+}
+
+/** \} */ // Manipulator Creation API
+
+
+/* -------------------------------------------------------------------- */
+
+/**
+ * Remove \a manipulator from selection.
+ * Reallocates memory for selected manipulators so better not call for selecting multiple ones.
+ *
+ * \return if the selection has changed.
+ */
+bool wm_manipulator_deselect(wmManipulatorMap *mmap, wmManipulator *manipulator)
+{
+ if (!mmap->mmap_context.selected_manipulator)
+ return false;
+
+ wmManipulator ***sel = &mmap->mmap_context.selected_manipulator;
+ int *tot_selected = &mmap->mmap_context.tot_selected;
+ bool changed = false;
+
+ /* caller should check! */
+ BLI_assert(manipulator->state & WM_MANIPULATOR_SELECTED);
+
+ /* remove manipulator from selected_manipulators array */
+ for (int i = 0; i < (*tot_selected); i++) {
+ if ((*sel)[i] == manipulator) {
+ for (int j = i; j < ((*tot_selected) - 1); j++) {
+ (*sel)[j] = (*sel)[j + 1];
+ }
+ changed = true;
+ break;
+ }
+ }
+
+ /* update array data */
+ if ((*tot_selected) <= 1) {
+ wm_manipulatormap_selected_delete(mmap);
+ }
+ else {
+ *sel = MEM_reallocN(*sel, sizeof(**sel) * (*tot_selected));
+ (*tot_selected)--;
+ }
+
+ manipulator->state &= ~WM_MANIPULATOR_SELECTED;
+ return changed;
+}
+
+/**
+ * Add \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(bContext *C, wmManipulatorMap *mmap, wmManipulator *manipulator)
+{
+ wmManipulator ***sel = &mmap->mmap_context.selected_manipulator;
+ int *tot_selected = &mmap->mmap_context.tot_selected;
+
+ if (!manipulator || (manipulator->state & WM_MANIPULATOR_SELECTED))
+ return false;
+
+ (*tot_selected)++;
+
+ *sel = MEM_reallocN(*sel, sizeof(wmManipulator *) * (*tot_selected));
+ (*sel)[(*tot_selected) - 1] = manipulator;
+
+ manipulator->state |= WM_MANIPULATOR_SELECTED;
+ if (manipulator->select) {
+ manipulator->select(C, manipulator, SEL_SELECT);
+ }
+ wm_manipulatormap_set_highlighted_manipulator(mmap, C, manipulator, manipulator->highlighted_part);
+
+ return true;
+}
+
+void wm_manipulator_calculate_scale(wmManipulator *manipulator, const bContext *C)
+{
+ const RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ float scale = 1.0f;
+
+ if (manipulator->mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCALE_3D) {
+ if (rv3d /*&& (U.manipulator_flag & V3D_3D_MANIPULATORS) == 0*/) { /* UserPref flag might be useful for later */
+ if (manipulator->get_final_position) {
+ float position[3];
+
+ manipulator->get_final_position(manipulator, position);
+ scale = ED_view3d_pixel_size(rv3d, position) * (float)U.manipulator_scale;
+ }
+ else {
+ scale = ED_view3d_pixel_size(rv3d, manipulator->origin) * (float)U.manipulator_scale;
+ }
+ }
+ else {
+ scale = U.manipulator_scale * 0.02f;
+ }
+ }
+
+ manipulator->scale = scale * manipulator->user_scale;
+}
+
+static void manipulator_update_prop_data(wmManipulator *manipulator)
+{
+ /* manipulator property might have been changed, so update manipulator */
+ if (manipulator->props && manipulator->prop_data_update) {
+ for (int i = 0; i < manipulator->max_prop; i++) {
+ if (manipulator->props[i]) {
+ manipulator->prop_data_update(manipulator, i);
+ }
+ }
+ }
+}
+
+void wm_manipulator_update(wmManipulator *manipulator, const bContext *C, const bool refresh_map)
+{
+ if (refresh_map) {
+ manipulator_update_prop_data(manipulator);
+ }
+ wm_manipulator_calculate_scale(manipulator, C);
+}
+
+bool wm_manipulator_is_visible(wmManipulator *manipulator)
+{
+ if (manipulator->flag & WM_MANIPULATOR_HIDDEN) {
+ return false;
+ }
+ if ((manipulator->state & WM_MANIPULATOR_ACTIVE) &&
+ !(manipulator->flag & (WM_MANIPULATOR_DRAW_ACTIVE | WM_MANIPULATOR_DRAW_VALUE)))
+ {
+ /* don't draw while active (while dragging) */
+ return false;
+ }
+ if ((manipulator->flag & WM_MANIPULATOR_DRAW_HOVER) &&
+ !(manipulator->state & WM_MANIPULATOR_HIGHLIGHT) &&
+ !(manipulator->state & WM_MANIPULATOR_SELECTED)) /* still draw selected manipulators */
+ {
+ /* only draw on mouse hover */
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h b/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h
new file mode 100644
index 00000000000..1a9693a472b
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h
@@ -0,0 +1,230 @@
+/*
+ * ***** 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/manipulators/intern/wm_manipulator_intern.h
+ * \ingroup wm
+ */
+
+
+#ifndef __WM_MANIPULATOR_INTERN_H__
+#define __WM_MANIPULATOR_INTERN_H__
+
+struct wmKeyConfig;
+struct wmManipulatorMap;
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+/* manipulators are set per region by registering them on manipulator-maps */
+typedef struct wmManipulator {
+ struct wmManipulator *next, *prev;
+
+ char idname[MAX_NAME + 4]; /* + 4 for unique '.001', '.002', etc suffix */
+ /* pointer back to group this manipulator is in (just for quick access) */
+ struct wmManipulatorGroup *mgroup;
+
+ /* could become wmManipulatorType */
+ /* draw manipulator */
+ void (*draw)(const struct bContext *, struct wmManipulator *);
+
+ /* determine if the mouse intersects with the manipulator. The calculation should be done in the callback itself */
+ int (*intersect)(struct bContext *, const struct wmEvent *, struct wmManipulator *);
+
+ /* determines 3d intersection by rendering the manipulator in a selection routine. */
+ void (*render_3d_intersection)(const struct bContext *, struct wmManipulator *, int);
+
+ /* handler used by the manipulator. Usually handles interaction tied to a manipulator type */
+ int (*handler)(struct bContext *, const struct wmEvent *, struct wmManipulator *, const int);
+
+ /* manipulator-specific handler to update manipulator attributes based on the property value */
+ void (*prop_data_update)(struct wmManipulator *, int);
+
+ /* returns the final position which may be different from the origin, depending on the manipulator.
+ * used in calculations of scale */
+ void (*get_final_position)(struct wmManipulator *, float[]);
+
+ /* activate a manipulator state when the user clicks on it */
+ int (*invoke)(struct bContext *, const struct wmEvent *, struct wmManipulator *);
+
+ /* called when manipulator tweaking is done - used to free data and reset property when cancelling */
+ void (*exit)(struct bContext *, struct wmManipulator *, const bool );
+
+ int (*get_cursor)(struct wmManipulator *);
+
+ /* called when manipulator selection state changes */
+ wmManipulatorSelectFunc select;
+
+ int flag; /* flags that influence the behavior or how the manipulators are drawn */
+ short state; /* state flags (active, highlighted, selected) */
+
+ unsigned char highlighted_part;
+
+ /* center of manipulator in space, 2d or 3d */
+ float origin[3];
+ /* custom offset from origin */
+ float offset[3];
+ /* runtime property, set the scale while drawing on the viewport */
+ float scale;
+ /* user defined scale, in addition to the original one */
+ float user_scale;
+ /* user defined width for line drawing */
+ float line_width;
+ /* manipulator colors (uses default fallbacks if not defined) */
+ float col[4], col_hi[4];
+
+ /* data used during interaction */
+ void *interaction_data;
+
+ /* name of operator to spawn when activating the manipulator */
+ const char *opname;
+ /* operator properties if manipulator spawns and controls an operator,
+ * or owner pointer if manipulator spawns and controls a property */
+ PointerRNA opptr;
+
+ /* maximum number of properties attached to the manipulator */
+ int max_prop;
+ /* arrays of properties attached to various manipulator parameters. As
+ * the manipulator is interacted with, those properties get updated */
+ PointerRNA *ptr;
+ PropertyRNA **props;
+} wmManipulator;
+
+/* wmManipulator.state */
+enum {
+ WM_MANIPULATOR_HIGHLIGHT = (1 << 0), /* while hovered */
+ WM_MANIPULATOR_ACTIVE = (1 << 1), /* while dragging */
+ WM_MANIPULATOR_SELECTED = (1 << 2),
+};
+
+/**
+ * \brief Manipulator tweak flag.
+ * Bitflag passed to manipulator while tweaking.
+ */
+enum {
+ /* drag with extra precision (shift)
+ * NOTE: Manipulators are responsible for handling this (manipulator->handler callback)! */
+ WM_MANIPULATOR_TWEAK_PRECISE = (1 << 0),
+};
+
+void wm_manipulator_register(struct wmManipulatorGroup *mgroup, struct wmManipulator *manipulator, const char *name);
+
+bool wm_manipulator_deselect(struct wmManipulatorMap *mmap, struct wmManipulator *manipulator);
+bool wm_manipulator_select(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulator *manipulator);
+
+void wm_manipulator_calculate_scale(struct wmManipulator *manipulator, const bContext *C);
+void wm_manipulator_update(struct wmManipulator *manipulator, const bContext *C, const bool refresh_map);
+bool wm_manipulator_is_visible(struct wmManipulator *manipulator);
+
+void fix_linking_manipulator_arrow(void);
+void fix_linking_manipulator_arrow2d(void);
+void fix_linking_manipulator_cage(void);
+void fix_linking_manipulator_dial(void);
+void fix_linking_manipulator_facemap(void);
+void fix_linking_manipulator_primitive(void);
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+enum {
+ TWEAK_MODAL_CANCEL = 1,
+ TWEAK_MODAL_CONFIRM,
+ TWEAK_MODAL_PRECISION_ON,
+ TWEAK_MODAL_PRECISION_OFF,
+};
+
+struct wmManipulatorGroup *wm_manipulatorgroup_new_from_type(struct wmManipulatorGroupType *mgrouptype);
+void wm_manipulatorgroup_free(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulatorGroup *mgroup);
+void wm_manipulatorgroup_manipulator_register(struct wmManipulatorGroup *mgroup, wmManipulator *manipulator);
+wmManipulator *wm_manipulatorgroup_find_intersected_mainpulator(
+ const struct wmManipulatorGroup *mgroup, struct bContext *C, const struct wmEvent *event,
+ unsigned char *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(const struct wmManipulatorGroup *mgroup, const struct bContext *C);
+bool wm_manipulatorgroup_is_visible_in_drawstep(const struct wmManipulatorGroup *mgroup, const int drawstep);
+
+void wm_manipulatorgrouptype_keymap_init(struct wmManipulatorGroupType *mgrouptype, struct wmKeyConfig *keyconf);
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+typedef struct wmManipulatorMap {
+ struct wmManipulatorMap *next, *prev;
+
+ struct wmManipulatorMapType *type;
+ ListBase manipulator_groups;
+
+ char update_flag; /* private, update tagging */
+
+ /**
+ * \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 *highlighted_manipulator;
+ /* user has clicked this manipulator and it gets all input */
+ struct wmManipulator *active_manipulator;
+ /* array for all selected manipulators
+ * TODO check on using BLI_array */
+ struct wmManipulator **selected_manipulator;
+ int tot_selected;
+ } mmap_context;
+} wmManipulatorMap;
+
+/**
+ * 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.
+ */
+typedef struct wmManipulatorMapType {
+ struct wmManipulatorMapType *next, *prev;
+ char idname[64];
+ short spaceid, regionid;
+ /* types of manipulator-groups for this manipulator-map type */
+ ListBase manipulator_grouptypes;
+} wmManipulatorMapType;
+
+void wm_manipulatormap_selected_delete(wmManipulatorMap *mmap);
+bool wm_manipulatormap_deselect_all(struct wmManipulatorMap *mmap, wmManipulator ***sel);
+
+
+/* -------------------------------------------------------------------- */
+/* Manipulator drawing */
+
+typedef struct ManipulatorGeometryInfo {
+ int nverts;
+ int ntris;
+ float (*verts)[3];
+ float (*normals)[3];
+ unsigned short *indices;
+ bool init;
+} ManipulatorGeometryInfo;
+
+#endif /* __WM_MANIPULATOR_INTERN_H__ */
+
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c b/source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c
new file mode 100644
index 00000000000..37a3ca8b55c
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulatorgroup.c
@@ -0,0 +1,586 @@
+/*
+ * ***** 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/manipulators/intern/wm_manipulatorgroup.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 "BKE_context.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "BPY_extern.h"
+
+#include "ED_screen.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "wm_event_system.h"
+
+/* own includes */
+#include "wm_manipulator_wmapi.h"
+#include "wm_manipulator_intern.h"
+
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorGroup
+ *
+ * \{ */
+
+/* wmManipulatorGroup.flag */
+enum {
+ WM_MANIPULATORGROUP_INITIALIZED = (1 << 2), /* mgroup has been initialized */
+};
+
+/**
+ * Create a new manipulator-group from \a mgrouptype.
+ */
+wmManipulatorGroup *wm_manipulatorgroup_new_from_type(wmManipulatorGroupType *mgrouptype)
+{
+ wmManipulatorGroup *mgroup = MEM_callocN(sizeof(*mgroup), "manipulator-group");
+ mgroup->type = mgrouptype;
+
+ return mgroup;
+}
+
+void wm_manipulatorgroup_free(bContext *C, wmManipulatorMap *mmap, wmManipulatorGroup *mgroup)
+{
+ for (wmManipulator *manipulator = mgroup->manipulators.first; manipulator;) {
+ wmManipulator *manipulator_next = manipulator->next;
+ WM_manipulator_delete(&mgroup->manipulators, mmap, manipulator, C);
+ manipulator = manipulator_next;
+ }
+ BLI_assert(BLI_listbase_is_empty(&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->manipulator_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 *manipulator)
+{
+ BLI_assert(!BLI_findstring(&mgroup->manipulators, manipulator->idname, offsetof(wmManipulator, idname)));
+ BLI_addtail(&mgroup->manipulators, manipulator);
+ manipulator->mgroup = mgroup;
+}
+
+void wm_manipulatorgroup_attach_to_modal_handler(
+ bContext *C, wmEventHandler *handler,
+ wmManipulatorGroupType *mgrouptype, wmOperator *op)
+{
+ /* maybe overly careful, but manipulator-grouptype could come from a failed creation */
+ if (!mgrouptype) {
+ return;
+ }
+
+ /* now instantiate the manipulator-map */
+ mgrouptype->op = op;
+
+ /* try to find map in handler region that contains mgrouptype */
+ if (handler->op_region && handler->op_region->manipulator_map) {
+ handler->manipulator_map = handler->op_region->manipulator_map;
+ ED_region_tag_redraw(handler->op_region);
+ }
+
+ WM_event_add_mousemove(C);
+}
+
+wmManipulator *wm_manipulatorgroup_find_intersected_mainpulator(
+ const wmManipulatorGroup *mgroup, bContext *C, const wmEvent *event,
+ unsigned char *part)
+{
+ for (wmManipulator *manipulator = mgroup->manipulators.first; manipulator; manipulator = manipulator->next) {
+ if (manipulator->intersect && (manipulator->flag & WM_MANIPULATOR_HIDDEN) == 0) {
+ if ((*part = manipulator->intersect(C, event, manipulator))) {
+ return manipulator;
+ }
+ }
+ }
+
+ 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 *manipulator = mgroup->manipulators.first; manipulator; manipulator = manipulator->next) {
+ if ((manipulator->flag & WM_MANIPULATOR_HIDDEN) == 0) {
+ if (((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) && manipulator->render_3d_intersection) ||
+ ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) == 0 && manipulator->intersect))
+ {
+ BLI_addhead(listbase, BLI_genericNodeN(manipulator));
+ }
+ }
+ }
+}
+
+void wm_manipulatorgroup_ensure_initialized(wmManipulatorGroup *mgroup, const bContext *C)
+{
+ /* prepare for first draw */
+ if (UNLIKELY((mgroup->flag & WM_MANIPULATORGROUP_INITIALIZED) == 0)) {
+ mgroup->type->init(C, mgroup);
+ mgroup->flag |= WM_MANIPULATORGROUP_INITIALIZED;
+ }
+}
+
+bool wm_manipulatorgroup_is_visible(const wmManipulatorGroup *mgroup, const bContext *C)
+{
+ /* Check for poll function, if manipulator-group belongs to an operator, also check if the operator is running. */
+ return ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_OP) == 0 || mgroup->type->op) &&
+ (!mgroup->type->poll || mgroup->type->poll(C, mgroup->type));
+}
+
+bool wm_manipulatorgroup_is_visible_in_drawstep(const wmManipulatorGroup *mgroup, const int drawstep)
+{
+ switch (drawstep) {
+ case WM_MANIPULATORMAP_DRAWSTEP_2D:
+ return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) == 0;
+ case WM_MANIPULATORMAP_DRAWSTEP_3D:
+ return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D);
+ case WM_MANIPULATORMAP_DRAWSTEP_IN_SCENE:
+ return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SCENE_DEPTH);
+ default:
+ BLI_assert(0);
+ 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;
+ wmManipulator ***sel = &mmap->mmap_context.selected_manipulator;
+ wmManipulator *highlighted = mmap->mmap_context.highlighted_manipulator;
+
+ 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, sel);
+ BLI_assert(*sel == NULL && mmap->mmap_context.tot_selected == 0);
+ }
+
+ if (highlighted) {
+ const bool is_selected = (highlighted->state & WM_MANIPULATOR_SELECTED);
+ bool redraw = false;
+
+ if (toggle) {
+ /* toggle: deselect if already selected, else select */
+ deselect = is_selected;
+ }
+
+ if (deselect) {
+ if (is_selected && wm_manipulator_deselect(mmap, highlighted)) {
+ redraw = true;
+ }
+ }
+ else if (wm_manipulator_select(C, mmap, highlighted)) {
+ 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 = "Manipulator 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;
+ wmManipulator *active;
+
+ int init_event; /* initial event type */
+ int flag; /* tweak flags */
+} ManipulatorTweakData;
+
+static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel)
+{
+ ManipulatorTweakData *mtweak = op->customdata;
+ if (mtweak->active->exit) {
+ mtweak->active->exit(C, mtweak->active, cancel);
+ }
+ wm_manipulatormap_set_active_manipulator(mtweak->mmap, C, NULL, NULL);
+ MEM_freeN(mtweak);
+}
+
+static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ManipulatorTweakData *mtweak = op->customdata;
+ wmManipulator *manipulator = mtweak->active;
+
+ if (!manipulator) {
+ BLI_assert(0);
+ return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
+ }
+
+ if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
+ manipulator_tweak_finish(C, op, false);
+ return OPERATOR_FINISHED;
+ }
+
+
+ if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case TWEAK_MODAL_CANCEL:
+ manipulator_tweak_finish(C, op, true);
+ return OPERATOR_CANCELLED;
+ case TWEAK_MODAL_CONFIRM:
+ manipulator_tweak_finish(C, op, false);
+ return OPERATOR_FINISHED;
+ case TWEAK_MODAL_PRECISION_ON:
+ mtweak->flag |= WM_MANIPULATOR_TWEAK_PRECISE;
+ break;
+ case TWEAK_MODAL_PRECISION_OFF:
+ mtweak->flag &= ~WM_MANIPULATOR_TWEAK_PRECISE;
+ break;
+ }
+ }
+
+ /* handle manipulator */
+ if (manipulator->handler) {
+ manipulator->handler(C, event, manipulator, mtweak->flag);
+ }
+
+ /* Ugly hack to send manipulator events */
+ ((wmEvent *)event)->type = EVT_MANIPULATOR_UPDATE;
+
+ /* always return PASS_THROUGH so modal handlers
+ * with manipulators attached can update */
+ 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 *manipulator = mmap->mmap_context.highlighted_manipulator;
+
+ if (!manipulator) {
+ /* wm_handlers_do_intern shouldn't let this happen */
+ BLI_assert(0);
+ return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
+ }
+
+
+ /* activate highlighted manipulator */
+ wm_manipulatormap_set_active_manipulator(mmap, C, event, manipulator);
+
+ /* XXX temporary workaround for modal manipulator operator
+ * conflicting with modal operator attached to manipulator */
+ if (manipulator->opname) {
+ wmOperatorType *ot = WM_operatortype_find(manipulator->opname, true);
+ if (ot->modal) {
+ return OPERATOR_FINISHED;
+ }
+ }
+
+
+ ManipulatorTweakData *mtweak = MEM_mallocN(sizeof(ManipulatorTweakData), __func__);
+
+ mtweak->init_event = event->type;
+ mtweak->active = mmap->mmap_context.highlighted_manipulator;
+ mtweak->mmap = mmap;
+ mtweak->flag = 0;
+
+ op->customdata = mtweak;
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void MANIPULATORGROUP_OT_manipulator_tweak(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Manipulator Tweak";
+ ot->description = "Tweak the active manipulator";
+ ot->idname = "MANIPULATORGROUP_OT_manipulator_tweak";
+
+ /* api callbacks */
+ ot->invoke = manipulator_tweak_invoke;
+ ot->modal = manipulator_tweak_modal;
+
+ ot->flag = OPTYPE_UNDO;
+}
+
+/** \} */ // Manipulator operators
+
+
+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", ""},
+ {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_assign(keymap, "MANIPULATORGROUP_OT_manipulator_tweak");
+
+ return keymap;
+}
+
+/**
+ * Common default keymap for manipulator groups
+ */
+wmKeyMap *WM_manipulatorgroup_keymap_common(const struct wmManipulatorGroupType *mgrouptype, 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, mgrouptype->name, mgrouptype->spaceid, mgrouptype->regionid);
+
+ WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0);
+ manipulatorgroup_tweak_modal_keymap(config, mgrouptype->name);
+
+ return km;
+}
+
+/**
+ * Variation of #WM_manipulatorgroup_keymap_common but with keymap items for selection
+ */
+wmKeyMap *WM_manipulatorgroup_keymap_common_sel(const struct wmManipulatorGroupType *mgrouptype, 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, mgrouptype->name, mgrouptype->spaceid, mgrouptype->regionid);
+
+ WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0);
+ manipulatorgroup_tweak_modal_keymap(config, mgrouptype->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
+ *
+ * \{ */
+
+/**
+ * Use this for registering manipulators on startup. For runtime, use #WM_manipulatorgrouptype_append_runtime.
+ */
+wmManipulatorGroupType *WM_manipulatorgrouptype_append(
+ wmManipulatorMapType *mmaptype, void (*mgrouptype_func)(wmManipulatorGroupType *))
+{
+ wmManipulatorGroupType *mgrouptype = MEM_callocN(sizeof(wmManipulatorGroupType), "manipulator-group");
+
+ mgrouptype_func(mgrouptype);
+ mgrouptype->spaceid = mmaptype->spaceid;
+ mgrouptype->regionid = mmaptype->regionid;
+ BLI_strncpy(mgrouptype->mapidname, mmaptype->idname, MAX_NAME);
+ /* if not set, use default */
+ if (!mgrouptype->keymap_init) {
+ mgrouptype->keymap_init = WM_manipulatorgroup_keymap_common;
+ }
+
+ /* add the type for future created areas of the same type */
+ BLI_addtail(&mmaptype->manipulator_grouptypes, mgrouptype);
+ return mgrouptype;
+}
+
+/**
+ * Use this for registering manipulators on runtime.
+ */
+wmManipulatorGroupType *WM_manipulatorgrouptype_append_runtime(
+ const Main *main, wmManipulatorMapType *mmaptype,
+ void (*mgrouptype_func)(wmManipulatorGroupType *))
+{
+ wmManipulatorGroupType *mgrouptype = WM_manipulatorgrouptype_append(mmaptype, mgrouptype_func);
+
+ /* Main is missing on startup when we create new areas.
+ * So this is only called for manipulators initialized on runtime */
+ WM_manipulatorgrouptype_init_runtime(main, mmaptype, mgrouptype);
+
+ return mgrouptype;
+}
+
+void WM_manipulatorgrouptype_init_runtime(
+ const Main *bmain, wmManipulatorMapType *mmaptype,
+ wmManipulatorGroupType *mgrouptype)
+{
+ /* init keymap - on startup there's an extra call to init keymaps for 'permanent' manipulator-groups */
+ wm_manipulatorgrouptype_keymap_init(mgrouptype, ((wmWindowManager *)bmain->wm.first)->defaultconf);
+
+ /* 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->type == mmaptype) {
+ wmManipulatorGroup *mgroup = wm_manipulatorgroup_new_from_type(mgrouptype);
+
+ /* just add here, drawing will occur on next update */
+ BLI_addtail(&mmap->manipulator_groups, mgroup);
+ wm_manipulatormap_set_highlighted_manipulator(mmap, NULL, NULL, 0);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+}
+
+void WM_manipulatorgrouptype_unregister(bContext *C, Main *bmain, wmManipulatorGroupType *mgrouptype)
+{
+ 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;
+ wmManipulatorGroup *mgroup, *mgroup_next;
+
+ for (mgroup = mmap->manipulator_groups.first; mgroup; mgroup = mgroup_next) {
+ mgroup_next = mgroup->next;
+ if (mgroup->type == mgrouptype) {
+ wm_manipulatorgroup_free(C, mmap, mgroup);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ wmManipulatorMapType *mmaptype = WM_manipulatormaptype_find(&(const struct wmManipulatorMapType_Params) {
+ mgrouptype->mapidname, mgrouptype->spaceid,
+ mgrouptype->regionid});
+
+ BLI_remlink(&mmaptype->manipulator_grouptypes, mgrouptype);
+ mgrouptype->prev = mgrouptype->next = NULL;
+
+ MEM_freeN(mgrouptype);
+}
+
+void wm_manipulatorgrouptype_keymap_init(wmManipulatorGroupType *mgrouptype, wmKeyConfig *keyconf)
+{
+ mgrouptype->keymap = mgrouptype->keymap_init(mgrouptype, keyconf);
+}
+
+/** \} */ /* wmManipulatorGroupType */
diff --git a/source/blender/windowmanager/manipulators/intern/wm_manipulatormap.c b/source/blender/windowmanager/manipulators/intern/wm_manipulatormap.c
new file mode 100644
index 00000000000..20f2df10af8
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/intern/wm_manipulatormap.c
@@ -0,0 +1,760 @@
+/*
+ * ***** 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/manipulators/intern/wm_manipulatormap.c
+ * \ingroup wm
+ */
+
+#include <string.h>
+
+#include "BKE_context.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+#include "BLI_ghash.h"
+
+#include "ED_screen.h"
+#include "ED_view3d.h"
+
+#include "GPU_glew.h"
+#include "GPU_select.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "wm_event_system.h"
+
+/* own includes */
+#include "wm_manipulator_wmapi.h"
+#include "wm_manipulator_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};
+
+/**
+ * Manipulator-map update tagging.
+ */
+enum eManipulatorMapUpdateFlags {
+ /* Tag manipulator-map for refresh. */
+ MANIPULATORMAP_REFRESH = (1 << 0),
+};
+
+
+/* -------------------------------------------------------------------- */
+/** \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 *mmaptype = WM_manipulatormaptype_ensure(mmap_params);
+ wmManipulatorMap *mmap;
+
+ mmap = MEM_callocN(sizeof(wmManipulatorMap), "ManipulatorMap");
+ mmap->type = mmaptype;
+ mmap->update_flag = MANIPULATORMAP_REFRESH;
+
+ /* create all manipulator-groups for this manipulator-map. We may create an empty one
+ * too in anticipation of manipulators from operators etc */
+ for (wmManipulatorGroupType *mgrouptype = mmaptype->manipulator_grouptypes.first;
+ mgrouptype;
+ mgrouptype = mgrouptype->next)
+ {
+ wmManipulatorGroup *mgroup = wm_manipulatorgroup_new_from_type(mgrouptype);
+ BLI_addtail(&mmap->manipulator_groups, mgroup);
+ }
+
+ return mmap;
+}
+
+void wm_manipulatormap_selected_delete(wmManipulatorMap *mmap)
+{
+ MEM_SAFE_FREE(mmap->mmap_context.selected_manipulator);
+ mmap->mmap_context.tot_selected = 0;
+}
+
+void wm_manipulatormap_delete(wmManipulatorMap *mmap)
+{
+ if (!mmap)
+ return;
+
+ for (wmManipulatorGroup *mgroup = mmap->manipulator_groups.first, *mgroup_next; mgroup; mgroup = mgroup_next) {
+ mgroup_next = mgroup->next;
+ wm_manipulatorgroup_free(NULL, mmap, mgroup);
+ }
+ BLI_assert(BLI_listbase_is_empty(&mmap->manipulator_groups));
+
+ wm_manipulatormap_selected_delete(mmap);
+
+ MEM_freeN(mmap);
+}
+
+/**
+ * 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
+ */
+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_str_new(__func__);
+
+ /* collect manipulators */
+ for (wmManipulatorGroup *mgroup = mmap->manipulator_groups.first; mgroup; mgroup = mgroup->next) {
+ if (!mgroup->type->poll || mgroup->type->poll(C, mgroup->type)) {
+ for (wmManipulator *manipulator = mgroup->manipulators.first;
+ manipulator;
+ manipulator = manipulator->next)
+ {
+ if ((include_hidden || (manipulator->flag & WM_MANIPULATOR_HIDDEN) == 0) &&
+ (!poll || poll(manipulator, data)))
+ {
+ BLI_ghash_insert(hash, manipulator->idname, manipulator);
+ }
+ }
+ }
+ }
+
+ return hash;
+}
+
+void WM_manipulatormap_tag_refresh(wmManipulatorMap *mmap)
+{
+ if (mmap) {
+ mmap->update_flag |= MANIPULATORMAP_REFRESH;
+ }
+}
+
+static void manipulatormap_tag_updated(wmManipulatorMap *mmap)
+{
+ mmap->update_flag = 0;
+}
+
+static bool manipulator_prepare_drawing(
+ wmManipulatorMap *mmap, wmManipulator *manipulator,
+ const bContext *C, ListBase *draw_manipulators)
+{
+ if (!wm_manipulator_is_visible(manipulator)) {
+ /* skip */
+ }
+ else {
+ wm_manipulator_update(manipulator, C, (mmap->update_flag & MANIPULATORMAP_REFRESH) != 0);
+ BLI_addhead(draw_manipulators, BLI_genericNodeN(manipulator));
+ 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 int drawstep)
+{
+ if (!mmap || BLI_listbase_is_empty(&mmap->manipulator_groups))
+ return;
+ wmManipulator *active_manipulator = mmap->mmap_context.active_manipulator;
+
+ /* only active manipulator needs updating */
+ if (active_manipulator) {
+ if (manipulator_prepare_drawing(mmap, active_manipulator, C, draw_manipulators)) {
+ manipulatormap_tag_updated(mmap);
+ }
+ /* don't draw any other manipulators */
+ return;
+ }
+
+ for (wmManipulatorGroup *mgroup = mmap->manipulator_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_manipulatorgroup_is_visible(mgroup, C))
+ {
+ continue;
+ }
+
+ /* needs to be initialized on first draw */
+ wm_manipulatorgroup_ensure_initialized(mgroup, C);
+ /* update data if needed */
+ /* XXX weak: Manipulator-group may skip refreshing if it's invisible (map gets untagged nevertheless) */
+ if (mmap->update_flag & MANIPULATORMAP_REFRESH && mgroup->type->refresh) {
+ mgroup->type->refresh(C, mgroup);
+ }
+ /* prepare drawing */
+ if (mgroup->type->draw_prepare) {
+ mgroup->type->draw_prepare(C, mgroup);
+ }
+
+ for (wmManipulator *manipulator = mgroup->manipulators.first; manipulator; manipulator = manipulator->next) {
+ manipulator_prepare_drawing(mmap, manipulator, C, draw_manipulators);
+ }
+ }
+
+ manipulatormap_tag_updated(mmap);
+}
+
+/**
+ * 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)
+{
+ if (!mmap)
+ return;
+ BLI_assert(!BLI_listbase_is_empty(&mmap->manipulator_groups));
+
+ /* draw_manipulators contains all visible manipulators - draw them */
+ for (LinkData *link = draw_manipulators->first, *link_next; link; link = link_next) {
+ wmManipulator *manipulator = link->data;
+ link_next = link->next;
+
+ manipulator->draw(C, manipulator);
+ /* free/remove manipulator link after drawing */
+ BLI_freelinkN(draw_manipulators, link);
+ }
+}
+
+void WM_manipulatormap_draw(wmManipulatorMap *mmap, const bContext *C, const int drawstep)
+{
+ 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_find_active_3D_loop(const bContext *C, ListBase *visible_manipulators)
+{
+ int selectionbase = 0;
+ wmManipulator *manipulator;
+
+ for (LinkData *link = visible_manipulators->first; link; link = link->next) {
+ manipulator = link->data;
+ /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected manipulator part id */
+ manipulator->render_3d_intersection(C, manipulator, selectionbase << 8);
+
+ selectionbase++;
+ }
+}
+
+static int manipulator_find_intersected_3D_intern(
+ ListBase *visible_manipulators, const bContext *C, const int co[2],
+ const float hotspot)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *ar = CTX_wm_region(C);
+ View3D *v3d = sa->spacedata.first;
+ RegionView3D *rv3d = ar->regiondata;
+ rctf rect, selrect;
+ GLuint buffer[64]; // max 4 items per select, so large enuf
+ short hits;
+ const bool do_passes = GPU_select_query_check_active();
+
+ extern void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect);
+
+
+ rect.xmin = co[0] - hotspot;
+ rect.xmax = co[0] + hotspot;
+ rect.ymin = co[1] - hotspot;
+ rect.ymax = co[1] + hotspot;
+
+ selrect = rect;
+
+ view3d_winmatrix_set(ar, v3d, &rect);
+ mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat);
+
+ if (do_passes)
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &selrect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
+ else
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &selrect, GPU_SELECT_ALL, 0);
+ /* do the drawing */
+ manipulator_find_active_3D_loop(C, visible_manipulators);
+
+ hits = GPU_select_end();
+
+ if (do_passes) {
+ GPU_select_begin(buffer, ARRAY_SIZE(buffer), &selrect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
+ manipulator_find_active_3D_loop(C, visible_manipulators);
+ GPU_select_end();
+ }
+
+ view3d_winmatrix_set(ar, v3d, NULL);
+ mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat);
+
+ return hits > 0 ? buffer[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,
+ unsigned char *part)
+{
+ wmManipulator *result = NULL;
+ const float hotspot = 14.0f;
+ int ret;
+
+ *part = 0;
+ /* set up view matrices */
+ view3d_operator_needs_opengl(C);
+
+ ret = manipulator_find_intersected_3D_intern(visible_manipulators, C, co, 0.5f * hotspot);
+
+ if (ret != -1) {
+ LinkData *link;
+ int retsec;
+ retsec = manipulator_find_intersected_3D_intern(visible_manipulators, C, co, 0.2f * hotspot);
+
+ if (retsec != -1)
+ ret = retsec;
+
+ link = BLI_findlink(visible_manipulators, ret >> 8);
+ *part = ret & 255;
+ result = link->data;
+ }
+
+ 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_find_highlighted_manipulator(
+ wmManipulatorMap *mmap, bContext *C, const wmEvent *event,
+ unsigned char *part)
+{
+ wmManipulator *manipulator = NULL;
+ ListBase visible_3d_manipulators = {NULL};
+
+ for (wmManipulatorGroup *mgroup = mmap->manipulator_groups.first; mgroup; mgroup = mgroup->next) {
+ if (wm_manipulatorgroup_is_visible(mgroup, C)) {
+ if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_IS_3D) {
+ wm_manipulatorgroup_intersectable_manipulators_to_list(mgroup, &visible_3d_manipulators);
+ }
+ else if ((manipulator = wm_manipulatorgroup_find_intersected_mainpulator(mgroup, C, event, part))) {
+ break;
+ }
+ }
+ }
+
+ if (!BLI_listbase_is_empty(&visible_3d_manipulators)) {
+ manipulator = manipulator_find_intersected_3D(C, event->mval, &visible_3d_manipulators, part);
+ BLI_freelistN(&visible_3d_manipulators);
+ }
+
+ return manipulator;
+}
+
+void WM_manipulatormap_add_handlers(ARegion *ar, wmManipulatorMap *mmap)
+{
+ wmEventHandler *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 wmOperatorType *ot)
+{
+ const bool modal_running = (handler->op != NULL);
+
+ /* happens on render */
+ if (!handler->op_region || !handler->op_region->manipulator_map)
+ return;
+
+ /* hide operator manipulators */
+ if (!modal_running && ot->mgrouptype) {
+ ot->mgrouptype->op = NULL;
+ }
+
+ wmManipulatorMap *mmap = handler->op_region->manipulator_map;
+ wmManipulator *manipulator = wm_manipulatormap_get_active_manipulator(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) {
+ if (manipulator && manipulator->handler && manipulator->opname &&
+ STREQ(manipulator->opname, handler->op->idname))
+ {
+ manipulator->handler(C, event, manipulator, 0);
+ }
+ }
+ /* operator not running anymore */
+ else {
+ wm_manipulatormap_set_highlighted_manipulator(mmap, C, NULL, 0);
+ wm_manipulatormap_set_active_manipulator(mmap, C, event, NULL);
+ }
+
+ /* 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, wmManipulator ***sel)
+{
+ if (*sel == NULL || mmap->mmap_context.tot_selected == 0)
+ return false;
+
+ for (int i = 0; i < mmap->mmap_context.tot_selected; i++) {
+ (*sel)[i]->state &= ~WM_MANIPULATOR_SELECTED;
+ (*sel)[i] = NULL;
+ }
+ wm_manipulatormap_selected_delete(mmap);
+
+ /* always return true, we already checked
+ * if there's anything to deselect */
+ return true;
+}
+
+BLI_INLINE bool manipulator_selectable_poll(const wmManipulator *manipulator, void *UNUSED(data))
+{
+ return (manipulator->mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECTABLE);
+}
+
+/**
+ * Select all selectable manipulators in \a mmap.
+ * \return if selection has changed.
+ */
+static bool wm_manipulatormap_select_all_intern(
+ bContext *C, wmManipulatorMap *mmap, wmManipulator ***sel,
+ const int action)
+{
+ /* 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, *tot_sel = &mmap->mmap_context.tot_selected;
+ bool changed = false;
+
+ *tot_sel = BLI_ghash_size(hash);
+ *sel = MEM_reallocN(*sel, sizeof(**sel) * (*tot_sel));
+
+ GHASH_ITER_INDEX (gh_iter, hash, i) {
+ wmManipulator *manipulator_iter = BLI_ghashIterator_getValue(&gh_iter);
+
+ if ((manipulator_iter->state & WM_MANIPULATOR_SELECTED) == 0) {
+ changed = true;
+ }
+ manipulator_iter->state |= WM_MANIPULATOR_SELECTED;
+ if (manipulator_iter->select) {
+ manipulator_iter->select(C, manipulator_iter, action);
+ }
+ (*sel)[i] = manipulator_iter;
+ BLI_assert(i < (*tot_sel));
+ }
+ /* highlight first manipulator */
+ wm_manipulatormap_set_highlighted_manipulator(mmap, C, (*sel)[0], (*sel)[0]->highlighted_part);
+
+ 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)
+{
+ wmManipulator ***sel = &mmap->mmap_context.selected_manipulator;
+ bool changed = false;
+
+ switch (action) {
+ case SEL_SELECT:
+ changed = wm_manipulatormap_select_all_intern(C, mmap, sel, action);
+ break;
+ case SEL_DESELECT:
+ changed = wm_manipulatormap_deselect_all(mmap, sel);
+ 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)
+{
+ for (; mmap; mmap = mmap->next) {
+ wmManipulator *manipulator = mmap->mmap_context.highlighted_manipulator;
+ if (manipulator && manipulator->get_cursor) {
+ WM_cursor_set(win, manipulator->get_cursor(manipulator));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void wm_manipulatormap_set_highlighted_manipulator(
+ wmManipulatorMap *mmap, const bContext *C, wmManipulator *manipulator,
+ unsigned char part)
+{
+ if ((manipulator != mmap->mmap_context.highlighted_manipulator) ||
+ (manipulator && part != manipulator->highlighted_part))
+ {
+ if (mmap->mmap_context.highlighted_manipulator) {
+ mmap->mmap_context.highlighted_manipulator->state &= ~WM_MANIPULATOR_HIGHLIGHT;
+ mmap->mmap_context.highlighted_manipulator->highlighted_part = 0;
+ }
+
+ mmap->mmap_context.highlighted_manipulator = manipulator;
+
+ if (manipulator) {
+ manipulator->state |= WM_MANIPULATOR_HIGHLIGHT;
+ manipulator->highlighted_part = part;
+
+ if (C && manipulator->get_cursor) {
+ wmWindow *win = CTX_wm_window(C);
+ WM_cursor_set(win, manipulator->get_cursor(manipulator));
+ }
+ }
+ else {
+ if (C) {
+ wmWindow *win = CTX_wm_window(C);
+ WM_cursor_set(win, CURSOR_STD);
+ }
+ }
+
+ /* tag the region for redraw */
+ if (C) {
+ ARegion *ar = CTX_wm_region(C);
+ ED_region_tag_redraw(ar);
+ }
+ }
+}
+
+wmManipulator *wm_manipulatormap_get_highlighted_manipulator(wmManipulatorMap *mmap)
+{
+ return mmap->mmap_context.highlighted_manipulator;
+}
+
+void wm_manipulatormap_set_active_manipulator(
+ wmManipulatorMap *mmap, bContext *C, const wmEvent *event, wmManipulator *manipulator)
+{
+ if (manipulator && C) {
+ manipulator->state |= WM_MANIPULATOR_ACTIVE;
+ mmap->mmap_context.active_manipulator = manipulator;
+
+ if (manipulator->opname) {
+ wmOperatorType *ot = WM_operatortype_find(manipulator->opname, 0);
+
+ if (ot) {
+ /* first activate the manipulator itself */
+ if (manipulator->invoke && manipulator->handler) {
+ manipulator->invoke(C, event, manipulator);
+ }
+
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &manipulator->opptr);
+
+ /* we failed to hook the manipulator to the operator handler or operator was cancelled, return */
+ if (!mmap->mmap_context.active_manipulator) {
+ manipulator->state &= ~WM_MANIPULATOR_ACTIVE;
+ /* first activate the manipulator itself */
+ if (manipulator->interaction_data) {
+ MEM_freeN(manipulator->interaction_data);
+ manipulator->interaction_data = NULL;
+ }
+ }
+ return;
+ }
+ else {
+ printf("Manipulator error: operator not found");
+ mmap->mmap_context.active_manipulator = NULL;
+ return;
+ }
+ }
+ else {
+ if (manipulator->invoke && manipulator->handler) {
+ manipulator->invoke(C, event, manipulator);
+ }
+ }
+ WM_cursor_grab_enable(CTX_wm_window(C), true, true, NULL);
+ }
+ else {
+ manipulator = mmap->mmap_context.active_manipulator;
+
+
+ /* deactivate, manipulator but first take care of some stuff */
+ if (manipulator) {
+ manipulator->state &= ~WM_MANIPULATOR_ACTIVE;
+ /* first activate the manipulator itself */
+ if (manipulator->interaction_data) {
+ MEM_freeN(manipulator->interaction_data);
+ manipulator->interaction_data = NULL;
+ }
+ }
+ mmap->mmap_context.active_manipulator = NULL;
+
+ if (C) {
+ WM_cursor_grab_disable(CTX_wm_window(C), NULL);
+ ED_region_tag_redraw(CTX_wm_region(C));
+ WM_event_add_mousemove(C);
+ }
+ }
+}
+
+wmManipulator *wm_manipulatormap_get_active_manipulator(wmManipulatorMap *mmap)
+{
+ return mmap->mmap_context.active_manipulator;
+}
+
+/** \} */ /* wmManipulatorMap */
+
+
+/* -------------------------------------------------------------------- */
+/** \name wmManipulatorMapType
+ *
+ * \{ */
+
+wmManipulatorMapType *WM_manipulatormaptype_find(
+ const struct wmManipulatorMapType_Params *mmap_params)
+{
+ for (wmManipulatorMapType *mmaptype = manipulatormaptypes.first; mmaptype; mmaptype = mmaptype->next) {
+ if (mmaptype->spaceid == mmap_params->spaceid &&
+ mmaptype->regionid == mmap_params->regionid &&
+ STREQ(mmaptype->idname, mmap_params->idname))
+ {
+ return mmaptype;
+ }
+ }
+
+ return NULL;
+}
+
+wmManipulatorMapType *WM_manipulatormaptype_ensure(
+ const struct wmManipulatorMapType_Params *mmap_params)
+{
+ wmManipulatorMapType *mmaptype = WM_manipulatormaptype_find(mmap_params);
+
+ if (mmaptype) {
+ return mmaptype;
+ }
+
+ mmaptype = MEM_callocN(sizeof(wmManipulatorMapType), "manipulatortype list");
+ mmaptype->spaceid = mmap_params->spaceid;
+ mmaptype->regionid = mmap_params->regionid;
+ BLI_strncpy(mmaptype->idname, mmap_params->idname, sizeof(mmaptype->idname));
+ BLI_addhead(&manipulatormaptypes, mmaptype);
+
+ return mmaptype;
+}
+
+void wm_manipulatormaptypes_free(void)
+{
+ for (wmManipulatorMapType *mmaptype = manipulatormaptypes.first; mmaptype; mmaptype = mmaptype->next) {
+ BLI_freelistN(&mmaptype->manipulator_grouptypes);
+ }
+ BLI_freelistN(&manipulatormaptypes);
+}
+
+/**
+ * Initialize keymaps for all existing manipulator-groups
+ */
+void wm_manipulators_keymap(wmKeyConfig *keyconf)
+{
+ wmManipulatorMapType *mmaptype;
+ wmManipulatorGroupType *mgrouptype;
+
+ /* 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 (mmaptype = manipulatormaptypes.first; mmaptype; mmaptype = mmaptype->next) {
+ for (mgrouptype = mmaptype->manipulator_grouptypes.first; mgrouptype; mgrouptype = mgrouptype->next) {
+ wm_manipulatorgrouptype_keymap_init(mgrouptype, keyconf);
+ }
+ }
+}
+
+/** \} */ /* wmManipulatorMapType */
+
diff --git a/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h b/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h
new file mode 100644
index 00000000000..c5008cef896
--- /dev/null
+++ b/source/blender/windowmanager/manipulators/wm_manipulator_wmapi.h
@@ -0,0 +1,90 @@
+/*
+ * ***** 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/manipulators/wm_manipulator_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_MANIPULATOR_WMAPI_H__
+#define __WM_MANIPULATOR_WMAPI_H__
+
+struct wmEventHandler;
+struct wmManipulatorMap;
+struct wmOperatorType;
+struct wmOperator;
+
+
+/* -------------------------------------------------------------------- */
+/* wmManipulator */
+
+typedef void (*wmManipulatorSelectFunc)(struct bContext *, struct wmManipulator *, const int);
+
+struct wmManipulatorGroup *wm_manipulator_get_parent_group(const struct wmManipulator *manipulator);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorGroup */
+
+void MANIPULATORGROUP_OT_manipulator_select(struct wmOperatorType *ot);
+void MANIPULATORGROUP_OT_manipulator_tweak(struct wmOperatorType *ot);
+
+void wm_manipulatorgroup_attach_to_modal_handler(
+ struct bContext *C, struct wmEventHandler *handler,
+ struct wmManipulatorGroupType *mgrouptype, struct wmOperator *op);
+
+/* -------------------------------------------------------------------- */
+/* wmManipulatorMap */
+
+void wm_manipulatormap_delete(struct wmManipulatorMap *mmap);
+void wm_manipulatormaptypes_free(void);
+
+void wm_manipulators_keymap(struct wmKeyConfig *keyconf);
+
+void wm_manipulatormaps_handled_modal_update(
+ bContext *C, struct wmEvent *event, struct wmEventHandler *handler,
+ const struct wmOperatorType *ot);
+void wm_manipulatormap_handler_context(bContext *C, struct wmEventHandler *handler);
+
+struct wmManipulator *wm_manipulatormap_find_highlighted_manipulator(
+ struct wmManipulatorMap *mmap, bContext *C,
+ const struct wmEvent *event, unsigned char *part);
+void wm_manipulatormap_set_highlighted_manipulator(
+ struct wmManipulatorMap *mmap, const bContext *C,
+ struct wmManipulator *manipulator, unsigned char part);
+struct wmManipulator *wm_manipulatormap_get_highlighted_manipulator(struct wmManipulatorMap *mmap);
+void wm_manipulatormap_set_active_manipulator(
+ struct wmManipulatorMap *mmap, bContext *C,
+ const struct wmEvent *event, struct wmManipulator *manipulator);
+struct wmManipulator *wm_manipulatormap_get_active_manipulator(struct wmManipulatorMap *mmap);
+
+#endif /* __WM_MANIPULATOR_WMAPI_H__ */
+
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 2f06ddab1e8..3dd294128e2 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -34,6 +34,8 @@
struct wmWindow;
struct ReportList;
+#include "manipulators/wm_manipulator_wmapi.h"
+
typedef struct wmPaintCursor {
struct wmPaintCursor *next, *prev;
diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h
index efc01b1f8a8..abab7c55f44 100644
--- a/source/blender/windowmanager/wm_event_system.h
+++ b/source/blender/windowmanager/wm_event_system.h
@@ -68,7 +68,8 @@ typedef struct wmEventHandler {
/* drop box handler */
ListBase *dropboxes;
-
+ /* manipulator handler */
+ struct wmManipulatorMap *manipulator_map;
} wmEventHandler;
/* custom types for handlers, for signalling, freeing */
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index 3085f138846..9d34bc24e6c 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -339,6 +339,8 @@ enum {
EVT_DROP = 0x5023,
EVT_BUT_CANCEL = 0x5024,
+ /* could become manipulator callback */
+ EVT_MANIPULATOR_UPDATE = 0x5025,
/* ********** End of Blender internal events. ********** */
};