Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Thu, 25 Jan 2018 05:28:06 +0000 (16:28 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 25 Jan 2018 05:32:43 +0000 (16:32 +1100)
15 files changed:
1  2 
source/blender/blenkernel/intern/screen.c
source/blender/blenloader/intern/readfile.c
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_region_tooltip.c
source/blender/makesdna/DNA_screen_types.h
source/blender/windowmanager/CMakeLists.txt
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/WM_types.h
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/intern/wm_tooltip.c
source/blender/windowmanager/manipulators/WM_manipulator_api.h
source/blender/windowmanager/manipulators/intern/wm_manipulator_group.c
source/blender/windowmanager/manipulators/intern/wm_manipulator_intern.h
source/blender/windowmanager/manipulators/intern/wm_manipulator_map.c

index 6bd88099792ca5a7c801009f045a764d759dcf2e,78caf1fe1f3996ea44a4fb43182664e137300d8d..754f59d2e33d804c5aa360ac656a54bd86475b52
@@@ -428,7 -382,8 +428,10 @@@ void BKE_screen_free(bScreen *sc
        BLI_freelistN(&sc->edgebase);
        BLI_freelistN(&sc->areabase);
  
 +      BKE_previewimg_free(&sc->preview);
++      
+       /* Region and timer are freed by the window manager. */
+       MEM_SAFE_FREE(sc->tool_tip);
  }
  
  /* for depsgraph */
index 2cb799d39b90225f506740a82ff51498883af1e5,4c1beb78bbfadf1cb8bb311720e607cfea93d5dd..236b14af8b043cbc2d9202e32143652ff7a6df15
@@@ -6663,10 -6402,15 +6663,11 @@@ static void lib_link_screen(FileData *f
                        IDP_LibLinkProperty(sc->id.properties, fd);
                        id_us_ensure_real(&sc->id);
  
 +                      /* deprecated, but needed for versioning (will be NULL'ed then) */
                        sc->scene = newlibadr(fd, sc->id.lib, sc->scene);
  
 -                      /* this should not happen, but apparently it does somehow. Until we figure out the cause,
 -                       * just assign first available scene */
 -                      if (!sc->scene)
 -                              sc->scene = main->scene.first;
 -
                        sc->animtimer = NULL; /* saved in rare cases */
+                       sc->tool_tip = NULL;
                        sc->scrubbing = false;
                        
                        for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
index 5d7a45a7e4812b1ada2c9ce68995fb43ef6b8846,d6c6f0a2eb691ed8dd9cd97f806ca6b885f1ed13..6e09318314d08aca492e9d35ff6d969bc8a36283
@@@ -1105,19 -1081,19 +1105,21 @@@ void UI_context_active_but_prop_get_tem
          struct bContext *C,
          struct PointerRNA *r_ptr, struct PropertyRNA **r_prop);
  
+ uiBut *UI_region_active_but_get(struct ARegion *ar);
  /* Styled text draw */
  void UI_fontstyle_set(const struct uiFontStyle *fs);
 -void UI_fontstyle_draw_ex(
 -        const struct uiFontStyle *fs, const struct rcti *rect, const char *str,
 -        size_t len, float *r_xofs, float *r_yofs);
 -void UI_fontstyle_draw(const struct uiFontStyle *fs, const struct rcti *rect, const char *str);
 -void UI_fontstyle_draw_rotated(const struct uiFontStyle *fs, const struct rcti *rect, const char *str);
 -void UI_fontstyle_draw_simple(const struct uiFontStyle *fs, float x, float y, const char *str);
 +void UI_fontstyle_draw_ex(const struct uiFontStyle *fs, const struct rcti *rect, const char *str,
 +                          const unsigned char col[4], size_t len, float *r_xofs, float *r_yofs);
 +void UI_fontstyle_draw(const struct uiFontStyle *fs, const struct rcti *rect, const char *str,
 +                       const unsigned char col[4]);
 +void UI_fontstyle_draw_rotated(const struct uiFontStyle *fs, const struct rcti *rect, const char *str,
 +                               const unsigned char col[4]);
 +void UI_fontstyle_draw_simple(const struct uiFontStyle *fs, float x, float y, const char *str,
 +                              const unsigned char col[4]);
  void UI_fontstyle_draw_simple_backdrop(
          const struct uiFontStyle *fs, float x, float y, const char *str,
 -        const unsigned char fg[4], const unsigned char bg[4]);
 +        const float col_fg[4], const float col_bg[4]);
  
  int UI_fontstyle_string_width(const struct uiFontStyle *fs, const char *str);
  int UI_fontstyle_height_max(const struct uiFontStyle *fs);
@@@ -1150,8 -1126,7 +1152,8 @@@ void UI_butstore_unregister(uiButStore 
  
  /* ui_interface_region_tooltip.c */
  struct ARegion *UI_tooltip_create_from_button(struct bContext *C, struct ARegion *butregion, uiBut *but);
- void UI_tooltip_free(struct bContext *C, struct ARegion *ar);
 +struct ARegion *UI_tooltip_create_from_manipulator(struct bContext *C, struct wmManipulator *mpr);
+ void UI_tooltip_free(struct bContext *C, struct bScreen *sc, struct ARegion *ar);
  
  /* How long before a tool-tip shows. */
  #define UI_TOOLTIP_DELAY 0.5
index 9c1cee915e4815c25050e85e25e19926647b0a02,2f7359da308cb483b365bb1b510412eec16d305d..1e31837f08824494d3bbcbb3e4cbc2280a56f891
@@@ -7701,12 -7650,12 +7699,12 @@@ static bool button_modal_state(uiHandle
   */
  void UI_but_tooltip_refresh(bContext *C, uiBut *but)
  {
-       uiHandleButtonData *data;
-       data = but->active;
-       if (data && data->tooltip) {
-               UI_tooltip_free(C, data->tooltip);
-               data->tooltip = UI_tooltip_create_from_button(C, data->region, but);
+       uiHandleButtonData *data = but->active;
+       if (data) {
 -              bScreen *sc = data->window->screen;
++              bScreen *sc = WM_window_get_active_screen(data->window);
+               if (sc->tool_tip && sc->tool_tip->region) {
+                       WM_tooltip_refresh(C, data->window);
+               }
        }
  }
  
index 9a818efebee9ad9c72e18647694416131e4c6c97,1eec37372150d42a3171ffbff5af784a7a1032c8..07fbefa42e18b78a1d76a7198d5d5ac20be3ae82
@@@ -936,26 -756,9 +936,26 @@@ ARegion *UI_tooltip_create_from_button(
        return ui_tooltip_create_with_data(C, data, init_position, aspect);
  }
  
- void UI_tooltip_free(bContext *C, ARegion *ar)
 +ARegion *UI_tooltip_create_from_manipulator(bContext *C, wmManipulator *mpr)
 +{
 +      wmWindow *win = CTX_wm_window(C);
 +      const float aspect = 1.0f;
 +      float init_position[2];
 +
 +      uiTooltipData *data = ui_tooltip_data_from_manipulator(C, mpr);
 +      if (data == NULL) {
 +              return NULL;
 +      }
 +
 +      init_position[0] = win->eventstate->x;
 +      init_position[1] = win->eventstate->y;
 +
 +      return ui_tooltip_create_with_data(C, data, init_position, aspect);
 +}
 +
+ void UI_tooltip_free(bContext *C, bScreen *sc, ARegion *ar)
  {
-       ui_region_temp_remove(C, CTX_wm_screen(C), ar);
+       ui_region_temp_remove(C, sc, ar);
  }
  
  /** \} */
index dd716014f6676c13faae257b6ad96a118edc52d8,a7718883438094d05a90c1802aa3154cc05f15e9..375641d11154c70efc5f17f2c1b1cc5b95d217be
@@@ -45,7 -44,8 +45,9 @@@ struct PanelType
  struct Scene;
  struct uiLayout;
  struct wmTimer;
+ struct wmTooltipState;
 +
  typedef struct bScreen {
        ID id;
        
@@@ -77,7 -78,7 +79,9 @@@
        struct wmTimer *animtimer;                      /* if set, screen has timer handler added in window */
        void *context;                                          /* context callback */
  
+       struct wmTooltipState *tool_tip;        /* runtime */
++
 +      PreviewImage *preview;
  } bScreen;
  
  typedef struct ScrVert {
index b5784fe543cc2b6aa360064f19737187690fa37d,c9278822b9a925ec97a73049ab9ac1500cd36d7b..059055daea9fe0e65876f2a34ac6a2baeb7ebe83
@@@ -70,18 -67,9 +70,19 @@@ set(SR
        intern/wm_operator_props.c
        intern/wm_operators.c
        intern/wm_subwindow.c
 -      intern/wm_tooltip.c
        intern/wm_window.c
        intern/wm_stereo.c
 +      intern/wm_toolsystem.c
++      intern/wm_tooltip.c
 +      manipulators/intern/wm_manipulator.c
 +      manipulators/intern/wm_manipulator_group.c
 +      manipulators/intern/wm_manipulator_group_type.c
 +      manipulators/intern/wm_manipulator_map.c
 +      manipulators/intern/wm_manipulator_target_props.c
 +      manipulators/intern/wm_manipulator_type.c
 +      message_bus/intern/wm_message_bus.c
 +      message_bus/intern/wm_message_bus_rna.c
 +      message_bus/intern/wm_message_bus_static.c
  
        WM_api.h
        WM_keymap.h
index 85f94721d4017fa4e64e7c443a82a55901258000,965eb2b258a2e4b4bae259378c91b38c92529097..7a66cc040145184e412d135f09cec84074261229
@@@ -569,13 -536,17 +569,24 @@@ bool        WM_event_is_tablet(const st
  bool        WM_event_is_ime_switch(const struct wmEvent *event);
  #endif
  
 +/* wm_toolsystem.c  */
 +void WM_toolsystem_unlink(struct bContext *C, struct WorkSpace *workspace);
 +void WM_toolsystem_link(struct bContext *C, struct WorkSpace *workspace);
 +
 +void WM_toolsystem_set(struct bContext *C, const struct bToolDef *tool);
 +void WM_toolsystem_init(struct bContext *C);
 +
+ /* wm_tooltip.c */
+ typedef struct ARegion *(*wmTooltipInitFn)(struct bContext *, struct ARegion *, bool *);
+ void WM_tooltip_timer_init(
+         struct bContext *C, struct wmWindow *win, struct ARegion *ar,
+         wmTooltipInitFn init);
+ void WM_tooltip_timer_clear(struct bContext *C, struct wmWindow *win);
+ void WM_tooltip_clear(struct bContext *C, struct wmWindow *win);
+ void WM_tooltip_init(struct bContext *C, struct wmWindow *win);
+ void WM_tooltip_refresh(struct bContext *C, struct wmWindow *win);
  #ifdef __cplusplus
  }
  #endif
index 6e559ef7157d746ca15317b9a47211685021bc63,d36702b4df7628b62db141c20ec4b8458219d60d..133ac6564f801ccc6865b9c71e2430b1683bfa31
@@@ -2262,156 -2164,6 +2262,139 @@@ static int wm_handlers_do_intern(bConte
                                        }
                                }
                        }
-                               if (event->type == MOUSEMOVE) {
-                                       WM_manipulatormap_tooltip_clear(C, mmap);
-                               }
 +                      else if (handler->manipulator_map) {
 +                              ScrArea *area = CTX_wm_area(C);
 +                              ARegion *region = CTX_wm_region(C);
 +                              wmManipulatorMap *mmap = handler->manipulator_map;
 +                              wmManipulator *mpr = wm_manipulatormap_highlight_get(mmap);
 +
 +                              if (region->manipulator_map != handler->manipulator_map) {
 +                                      WM_manipulatormap_tag_refresh(handler->manipulator_map);
 +                              }
 +
 +                              wm_manipulatormap_handler_context(C, handler);
 +                              wm_region_mouse_co(C, event);
 +
-                                               WM_manipulatormap_tooltip_timer_init(C, mmap);
-                                       }
-                               }
-                               /* handle user configurable manipulator-map keymap */
-                               else if ((event->type == TIMER) &&
-                                        (event->customdata == WM_manipulatormap_tooltip_timer_get(mmap)))
-                               {
-                                       if (mpr) {
-                                               if (mpr->state & WM_MANIPULATOR_STATE_MODAL) {
-                                                       WM_manipulatormap_tooltip_clear(C, mmap);
-                                               }
-                                               else {
-                                                       WM_manipulatormap_tooltip_create(C, mmap);
-                                               }
 +                              /* handle manipulator highlighting */
 +                              if (event->type == MOUSEMOVE && !wm_manipulatormap_modal_get(mmap)) {
 +                                      int part;
 +                                      mpr = wm_manipulatormap_highlight_find(mmap, C, event, &part);
 +                                      wm_manipulatormap_highlight_set(mmap, C, mpr, part);
 +                                      if (mpr != NULL) {
++                                              WM_tooltip_timer_init(C, CTX_wm_window(C), region, WM_manipulatormap_tooltip_init);
 +                                      }
 +                              }
 +                              else {
 +                                      /* Either we operate on a single highlighted item
 +                                       * or groups attached to the selected manipulators.
 +                                       * To simplify things both cases loop over an array of items. */
 +                                      wmManipulatorGroup *mgroup_first;
 +                                      bool is_mgroup_single;
 +
 +                                      if (ISMOUSE(event->type)) {
 +                                              /* Keep mpr set as-is, just fake single selection. */
 +                                              if (mpr) {
 +                                                      mgroup_first = mpr->parent_mgroup;
 +                                              }
 +                                              else {
 +                                                      mgroup_first = NULL;
 +                                              }
 +                                              is_mgroup_single = true;
 +                                      }
 +                                      else {
 +                                              if (WM_manipulatormap_is_any_selected(mmap)) {
 +                                                      const ListBase *groups = WM_manipulatormap_group_list(mmap);
 +                                                      mgroup_first = groups->first;
 +                                              }
 +                                              else {
 +                                                      mgroup_first = NULL;
 +                                              }
 +                                              is_mgroup_single = false;
 +                                      }
 +
 +                                      /* Don't use from now on. */
 +                                      mpr = NULL;
 +
 +                                      for (wmManipulatorGroup *mgroup = mgroup_first; mgroup; mgroup = mgroup->next) {
 +                                              /* get user customized keymap from default one */
 +
 +                                              if ((is_mgroup_single == false) &&
 +                                                  /* We might want to change the logic here and use some kind of manipulator edit-mode.
 +                                                   * For now just use keymap when a selection exists. */
 +                                                  wm_manipulatorgroup_is_any_selected(mgroup) == false)
 +                                              {
 +                                                      continue;
 +                                              }
 +
 +                                              const wmKeyMap *keymap = WM_keymap_active(wm, mgroup->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;
 +
 +                                                                      CTX_wm_manipulator_group_set(C, mgroup);
 +
 +                                                                      /* 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;
 +
 +                                                                      CTX_wm_manipulator_group_set(C, NULL);
 +
 +                                                                      if (action & WM_HANDLER_BREAK) {
 +                                                                              if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
 +                                                                                      printf("%s:       handled - and pass on! '%s'\n",
 +                                                                                             __func__, kmi->idname);
 +                                                                              }
 +                                                                              break;
 +                                                                      }
 +                                                                      else {
 +                                                                              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");
 +                                              }
 +
 +                                              if (action & WM_HANDLER_BREAK) {
 +                                                      break;
 +                                              }
 +
 +                                              if (is_mgroup_single) {
 +                                                      break;
 +                                              }
 +                                      }
 +                              }
 +
 +                              /* 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);
@@@ -2735,6 -2474,13 +2718,13 @@@ void wm_event_do_handlers(bContext *C
  
                        CTX_wm_window_set(C, win);
  
 -                      if (win->screen->tool_tip && win->screen->tool_tip->exit_on_event) {
+                       /* Clear tool-tip on mouse move. */
++                      if (screen->tool_tip && screen->tool_tip->exit_on_event) {
+                               if (ISMOUSE(event->type)) {
+                                       WM_tooltip_clear(C, win);
+                               }
+                       }
                        /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
                        CTX_wm_area_set(C, area_event_inside(C, &event->x));
                        CTX_wm_region_set(C, region_event_inside(C, &event->x));
                        /* fileread case */
                        if (CTX_wm_window(C) == NULL)
                                return;
-                       
+                       /* check for a tooltip */
 -                      {
 -                              bScreen *screen = CTX_wm_window(C)->screen;
++                      if (screen == WM_window_get_active_screen(win)) {
+                               if (screen->tool_tip && screen->tool_tip->timer) {
+                                       if ((event->type == TIMER) && (event->customdata == screen->tool_tip->timer)) {
+                                               WM_tooltip_init(C, win);
+                                       }
+                               }
+                       }
                        /* check dragging, creates new event or frees, adds draw tag */
                        wm_event_drag_test(wm, win, event);
                        
index 0000000000000000000000000000000000000000,86ca95ef37747d2551a6b79dd892f5f14bd422ce..83d620d1522cdfbc2a30081f0e2d190796e617c2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,106 +1,106 @@@
 -      bScreen *screen = win->screen;
+ /*
+  * ***** BEGIN GPL LICENSE BLOCK *****
+  *
+  * This program is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU General Public License
+  * as published by the Free Software Foundation; either version 2
+  * of the License, or (at your option) any later version. 
+  *
+  * This program is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+  *
+  * ***** END GPL LICENSE BLOCK *****
+  */
+ /** \file blender/windowmanager/intern/wm_tooltip.c
+  *  \ingroup wm
+  *
+  * Manages a per-window tool-tip.
+  */
+ #include "MEM_guardedalloc.h"
+ #include "BLI_utildefines.h"
+ #include "BKE_context.h"
+ #include "ED_screen.h"
+ #include "UI_interface.h"
+ #include "WM_api.h"
+ #include "WM_types.h"
+ void WM_tooltip_timer_init(
+         bContext *C, wmWindow *win, ARegion *ar,
+         wmTooltipInitFn init)
+ {
 -      bScreen *screen = win->screen;
++      bScreen *screen = WM_window_get_active_screen(win);
+       wmWindowManager *wm = CTX_wm_manager(C);
+       if (screen->tool_tip == NULL) {
+               screen->tool_tip = MEM_callocN(sizeof(*screen->tool_tip), __func__);
+       }
+       screen->tool_tip->region_from = ar;
+       screen->tool_tip->timer = WM_event_add_timer(wm, win, TIMER, UI_TOOLTIP_DELAY);
+       screen->tool_tip->init = init;
+ }
+ void WM_tooltip_timer_clear(bContext *C, wmWindow *win)
+ {
+       wmWindowManager *wm = CTX_wm_manager(C);
 -      bScreen *screen = win->screen;
++      bScreen *screen = WM_window_get_active_screen(win);
+       if (screen->tool_tip != NULL) {
+               if (screen->tool_tip->timer != NULL) {
+                       WM_event_remove_timer(wm, win, screen->tool_tip->timer);
+                       screen->tool_tip->timer = NULL;
+               }
+       }
+ }
+ void WM_tooltip_clear(bContext *C, wmWindow *win)
+ {
+       WM_tooltip_timer_clear(C, win);
 -      bScreen *screen = win->screen;
++      bScreen *screen = WM_window_get_active_screen(win);
+       if (screen->tool_tip != NULL) {
+               if (screen->tool_tip->region) {
+                       UI_tooltip_free(C, screen, screen->tool_tip->region);
+                       screen->tool_tip->region = NULL;
+               }
+               MEM_freeN(screen->tool_tip);
+               screen->tool_tip = NULL;
+       }
+ }
+ void WM_tooltip_init(bContext *C, wmWindow *win)
+ {
+       WM_tooltip_timer_clear(C, win);
 -      bScreen *screen = win->screen;
++      bScreen *screen = WM_window_get_active_screen(win);
+       if (screen->tool_tip->region) {
+               UI_tooltip_free(C, screen, screen->tool_tip->region);
+               screen->tool_tip->region = NULL;
+       }
+       screen->tool_tip->region = screen->tool_tip->init(
+               C, screen->tool_tip->region_from, &screen->tool_tip->exit_on_event);
+       if (screen->tool_tip->region == NULL) {
+               WM_tooltip_clear(C, win);
+       }
+ }
+ void WM_tooltip_refresh(bContext *C, wmWindow *win)
+ {
+       WM_tooltip_timer_clear(C, win);
++      bScreen *screen = WM_window_get_active_screen(win);
+       if (screen->tool_tip != NULL) {
+               if (screen->tool_tip->region) {
+                       UI_tooltip_free(C, screen, screen->tool_tip->region);
+                       screen->tool_tip->region = NULL;
+               }
+               WM_tooltip_init(C, win);
+       }
+ }
index 097af2dadcc458b67d10c33f2962377f48bf0d7c,0000000000000000000000000000000000000000..53bee9c677560289b86495dee7ca302f43907049
mode 100644,000000..100644
--- /dev/null
@@@ -1,340 -1,0 +1,334 @@@
- void WM_manipulatormap_tooltip_create(
-         struct bContext *C, struct wmManipulatorMap *mmap);
- void WM_manipulatormap_tooltip_clear(
-         struct bContext *C, struct wmManipulatorMap *mmap);
- void WM_manipulatormap_tooltip_timer_init(
-         struct bContext *C, struct wmManipulatorMap *mmap);
- const void *WM_manipulatormap_tooltip_timer_get(
-         struct wmManipulatorMap *mmap);
 +/*
 + * ***** 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 GHashIterator;
 +struct IDProperty;
 +struct Main;
 +struct PropertyRNA;
 +struct wmKeyConfig;
 +struct wmManipulator;
 +struct wmManipulatorProperty;
 +struct wmManipulatorPropertyType;
 +struct wmManipulatorType;
 +struct wmManipulatorGroup;
 +struct wmManipulatorGroupType;
 +struct wmManipulatorMap;
 +struct wmManipulatorMapType;
 +struct wmManipulatorMapType_Params;
 +struct wmMsgSubscribeKey;
 +struct wmMsgSubscribeValue;
 +
 +#include "wm_manipulator_fn.h"
 +
 +/* -------------------------------------------------------------------- */
 +/* wmManipulator */
 +
 +struct wmManipulator *WM_manipulator_new_ptr(
 +        const struct wmManipulatorType *wt, struct wmManipulatorGroup *mgroup,
 +        struct PointerRNA *properties);
 +struct wmManipulator *WM_manipulator_new(
 +        const char *idname, struct wmManipulatorGroup *mgroup,
 +        struct PointerRNA *properties);
 +void WM_manipulator_free(struct wmManipulator *mpr);
 +void WM_manipulator_unlink(
 +        ListBase *manipulatorlist, struct wmManipulatorMap *mmap, struct wmManipulator *mpr,
 +        struct bContext *C);
 +
 +void WM_manipulator_name_set(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr, const char *name);
 +
 +bool WM_manipulator_select_unlink(struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
 +bool WM_manipulator_select_set(struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select);
 +void WM_manipulator_highlight_set(struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
 +
 +struct wmManipulatorOpElem *WM_manipulator_operator_get(
 +        struct wmManipulator *mpr, int part_index);
 +struct PointerRNA *WM_manipulator_operator_set(
 +        struct wmManipulator *mpr, int part_index,
 +        struct wmOperatorType *ot, struct IDProperty *properties);
 +
 +/* callbacks */
 +void WM_manipulator_set_fn_custom_modal(struct wmManipulator *mpr, wmManipulatorFnModal fn);
 +
 +void WM_manipulator_set_matrix_location(
 +        struct wmManipulator *mpr, const float origin[3]);
 +void WM_manipulator_set_matrix_rotation_from_z_axis(
 +        struct wmManipulator *mpr, const float z_axis[3]);
 +void WM_manipulator_set_matrix_rotation_from_yz_axis(
 +        struct wmManipulator *mpr, const float y_axis[3], const float z_axis[3]);
 +
 +void WM_manipulator_set_matrix_offset_location(
 +        struct wmManipulator *mpr, const float origin[3]);
 +void WM_manipulator_set_matrix_offset_rotation_from_z_axis(
 +        struct wmManipulator *mpr, const float z_axis[3]);
 +void WM_manipulator_set_matrix_offset_rotation_from_yz_axis(
 +        struct wmManipulator *mpr, const float y_axis[3], const float z_axis[3]);
 +
 +void WM_manipulator_set_flag(struct wmManipulator *mpr, const int flag, const bool enable);
 +void WM_manipulator_set_scale(struct wmManipulator *mpr, float scale);
 +void WM_manipulator_set_line_width(struct wmManipulator *mpr, const float line_width);
 +
 +void WM_manipulator_get_color(const struct wmManipulator *mpr, float color[4]);
 +void WM_manipulator_set_color(struct wmManipulator *mpr, const float color[4]);
 +void WM_manipulator_get_color_highlight(const struct wmManipulator *mpr, float color_hi[4]);
 +void WM_manipulator_set_color_highlight(struct wmManipulator *mpr, const float color[4]);
 +
 +/**
 + * Leaving values NULL use values from #wmManipulator.
 + */
 +struct WM_ManipulatorMatrixParams {
 +      const float(*matrix_space)[4];
 +      const float(*matrix_basis)[4];
 +      const float(*matrix_offset)[4];
 +      const float *scale_final;
 +};
 +
 +void WM_manipulator_calc_matrix_final_params(
 +        const struct wmManipulator *mpr, const struct WM_ManipulatorMatrixParams *params,
 +        float r_mat[4][4]);
 +
 +void WM_manipulator_calc_matrix_final(const struct wmManipulator *mpr, float r_mat[4][4]);
 +
 +/* properties */
 +void WM_manipulator_properties_create_ptr(struct PointerRNA *ptr, struct wmManipulatorType *wt);
 +void WM_manipulator_properties_create(struct PointerRNA *ptr, const char *opstring);
 +void WM_manipulator_properties_alloc(struct PointerRNA **ptr, struct IDProperty **properties, const char *wtstring);
 +void WM_manipulator_properties_sanitize(struct PointerRNA *ptr, const bool no_context);
 +bool WM_manipulator_properties_default(struct PointerRNA *ptr, const bool do_update);
 +void WM_manipulator_properties_reset(struct wmManipulator *op);
 +void WM_manipulator_properties_clear(struct PointerRNA *ptr);
 +void WM_manipulator_properties_free(struct PointerRNA *ptr);
 +
 +
 +/* wm_manipulator_type.c */
 +const struct wmManipulatorType *WM_manipulatortype_find(const char *idname, bool quiet);
 +void WM_manipulatortype_append(void (*wtfunc)(struct wmManipulatorType *));
 +void WM_manipulatortype_append_ptr(void (*mnpfunc)(struct wmManipulatorType *, void *), void *userdata);
 +bool WM_manipulatortype_remove(struct bContext *C, struct Main *bmain, const char *idname);
 +void WM_manipulatortype_remove_ptr(struct bContext *C, struct Main *bmain, struct wmManipulatorType *wt);
 +void WM_manipulatortype_iter(struct GHashIterator *ghi);
 +
 +/* wm_manipulator_group_type.c */
 +struct wmManipulatorGroupType *WM_manipulatorgrouptype_find(const char *idname, bool quiet);
 +struct wmManipulatorGroupType *WM_manipulatorgrouptype_append(void (*wtfunc)(struct wmManipulatorGroupType *));
 +struct wmManipulatorGroupType *WM_manipulatorgrouptype_append_ptr(void (*mnpfunc)(struct wmManipulatorGroupType *, void *), void *userdata);
 +bool WM_manipulatorgrouptype_free(const char *idname);
 +void WM_manipulatorgrouptype_free_ptr(struct wmManipulatorGroupType *wt);
 +void WM_manipulatorgrouptype_iter(struct GHashIterator *ghi);
 +
 +struct wmManipulatorGroupTypeRef *WM_manipulatorgrouptype_append_and_link(
 +        struct wmManipulatorMapType *mmap_type,
 +        void (*wtfunc)(struct wmManipulatorGroupType *));
 +
 +/* wm_manipulator_map.c */
 +
 +/* Dynamic Updates (for RNA runtime registration) */
 +void WM_manipulatorconfig_update_tag_init(struct wmManipulatorMapType *mmap_type, struct wmManipulatorGroupType *wgt);
 +void WM_manipulatorconfig_update_tag_remove(struct wmManipulatorMapType *mmap_type, struct wmManipulatorGroupType *wgt);
 +void WM_manipulatorconfig_update(struct Main *bmain);
 +
 +
 +/* wm_maniulator_target_props.c */
 +struct wmManipulatorProperty *WM_manipulator_target_property_array(struct wmManipulator *mpr);
 +struct wmManipulatorProperty *WM_manipulator_target_property_at_index(
 +        struct wmManipulator *mpr, int index);
 +struct wmManipulatorProperty *WM_manipulator_target_property_find(
 +        struct wmManipulator *mpr, const char *idname);
 +
 +void WM_manipulator_target_property_def_rna_ptr(
 +        struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type,
 +        struct PointerRNA *ptr, struct PropertyRNA *prop, int index);
 +void WM_manipulator_target_property_def_rna(
 +        struct wmManipulator *mpr, const char *idname,
 +        struct PointerRNA *ptr, const char *propname, int index);
 +
 +void WM_manipulator_target_property_def_func_ptr(
 +        struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type,
 +        const struct wmManipulatorPropertyFnParams *params);
 +void WM_manipulator_target_property_def_func(
 +        struct wmManipulator *mpr, const char *idname,
 +        const struct wmManipulatorPropertyFnParams *params);
 +
 +void WM_manipulator_target_property_clear_rna_ptr(
 +        struct wmManipulator *mpr, const struct wmManipulatorPropertyType *mpr_prop_type);
 +void WM_manipulator_target_property_clear_rna(
 +        struct wmManipulator *mpr, const char *idname);
 +
 +bool WM_manipulator_target_property_is_valid_any(struct wmManipulator *mpr);
 +bool WM_manipulator_target_property_is_valid(
 +        const struct wmManipulatorProperty *mpr_prop);
 +float WM_manipulator_target_property_value_get(
 +        const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop);
 +void  WM_manipulator_target_property_value_set(
 +        struct bContext *C, const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
 +        const float value);
 +
 +void WM_manipulator_target_property_value_get_array(
 +        const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
 +        float *value);
 +void WM_manipulator_target_property_value_set_array(
 +        struct bContext *C, const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
 +        const float *value);
 +
 +bool WM_manipulator_target_property_range_get(
 +        const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop,
 +        float range[2]);
 +
 +int WM_manipulator_target_property_array_length(
 +        const struct wmManipulator *mpr, struct wmManipulatorProperty *mpr_prop);
 +
 +/* definitions */
 +const struct wmManipulatorPropertyType *WM_manipulatortype_target_property_find(
 +        const struct wmManipulatorType *wt, const char *idname);
 +void WM_manipulatortype_target_property_def(
 +        struct wmManipulatorType *wt, const char *idname, int data_type, int array_length);
 +
 +/* utilities */
 +void WM_manipulator_do_msg_notify_tag_refresh(
 +        struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val);
 +void WM_manipulator_target_property_subscribe_all(
 +        struct wmManipulator *mpr, struct wmMsgBus *mbus, struct ARegion *ar);
 +
 +/* -------------------------------------------------------------------- */
 +/* wmManipulatorGroup */
 +
 +/* Callbacks for 'wmManipulatorGroupType.setup_keymap' */
 +struct wmKeyMap *WM_manipulatorgroup_keymap_common(
 +        const struct wmManipulatorGroupType *wgt, struct wmKeyConfig *config);
 +struct wmKeyMap *WM_manipulatorgroup_keymap_common_select(
 +        const struct wmManipulatorGroupType *wgt, struct wmKeyConfig *config);
 +
 +
 +/* -------------------------------------------------------------------- */
 +/* wmManipulatorMap */
 +
 +struct wmManipulatorMap *WM_manipulatormap_new_from_type(
 +        const struct wmManipulatorMapType_Params *mmap_params);
 +const struct ListBase *WM_manipulatormap_group_list(struct wmManipulatorMap *mmap);
 +struct wmManipulatorGroup *WM_manipulatormap_group_find(
 +        struct wmManipulatorMap *mmap,
 +        const char *idname);
 +struct wmManipulatorGroup *WM_manipulatormap_group_find_ptr(
 +        struct wmManipulatorMap *mmap,
 +        const struct wmManipulatorGroupType *wgt);
 +void WM_manipulatormap_tag_refresh(struct wmManipulatorMap *mmap);
 +void WM_manipulatormap_draw(
 +        struct wmManipulatorMap *mmap, const struct bContext *C, const eWM_ManipulatorMapDrawStep drawstep);
 +void WM_manipulatormap_add_handlers(struct ARegion *ar, struct wmManipulatorMap *mmap);
 +bool WM_manipulatormap_select_all(struct bContext *C, struct wmManipulatorMap *mmap, const int action);
 +bool WM_manipulatormap_cursor_set(const struct wmManipulatorMap *mmap, struct wmWindow *win);
 +void WM_manipulatormap_message_subscribe(
 +        struct bContext *C, struct wmManipulatorMap *mmap, struct ARegion *ar, struct wmMsgBus *mbus);
 +bool WM_manipulatormap_is_any_selected(const struct wmManipulatorMap *mmap);
 +bool WM_manipulatormap_minmax(
 +        const struct wmManipulatorMap *mmap, bool use_hidden, bool use_select,
 +        float r_min[3], float r_max[3]);
 +
++struct ARegion *WM_manipulatormap_tooltip_init(
++        struct bContext *C, struct ARegion *ar, bool *r_exit_on_event);
 +
 +/* -------------------------------------------------------------------- */
 +/* wmManipulatorMapType */
 +
 +struct wmManipulatorMapType *WM_manipulatormaptype_find(
 +        const struct wmManipulatorMapType_Params *mmap_params);
 +struct wmManipulatorMapType *WM_manipulatormaptype_ensure(
 +        const struct wmManipulatorMapType_Params *mmap_params);
 +
 +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find(
 +        struct wmManipulatorMapType *mmap_type,
 +        const char *idname);
 +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr(
 +        struct wmManipulatorMapType *mmap_type,
 +        const struct wmManipulatorGroupType *wgt);
 +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link(
 +        struct wmManipulatorMapType *mmap_type,
 +        const char *idname);
 +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr(
 +        struct wmManipulatorMapType *mmap_type,
 +        struct wmManipulatorGroupType *wgt);
 +
 +void WM_manipulatormaptype_group_init_runtime_keymap(
 +        const struct Main *bmain,
 +        struct wmManipulatorGroupType *wgt);
 +void WM_manipulatormaptype_group_init_runtime(
 +        const struct Main *bmain, struct wmManipulatorMapType *mmap_type,
 +        struct wmManipulatorGroupType *wgt);
 +void WM_manipulatormaptype_group_unlink(
 +        struct bContext *C, struct Main *bmain, struct wmManipulatorMapType *mmap_type,
 +        const struct wmManipulatorGroupType *wgt);
 +
 +void WM_manipulatormaptype_group_free(struct wmManipulatorGroupTypeRef *wgt);
 +
 +/* -------------------------------------------------------------------- */
 +/* ManipulatorGroup */
 +
 +/* Add/Ensure/Remove (High level API) */
 +
 +void WM_manipulator_group_type_add_ptr_ex(
 +        struct wmManipulatorGroupType *wgt,
 +        struct wmManipulatorMapType *mmap_type);
 +void WM_manipulator_group_type_add_ptr(
 +        struct wmManipulatorGroupType *wgt);
 +void WM_manipulator_group_type_add(const char *idname);
 +
 +void WM_manipulator_group_type_ensure_ptr_ex(
 +        struct wmManipulatorGroupType *wgt,
 +        struct wmManipulatorMapType *mmap_type);
 +void WM_manipulator_group_type_ensure_ptr(
 +        struct wmManipulatorGroupType *wgt);
 +void WM_manipulator_group_type_ensure(const char *idname);
 +
 +void WM_manipulator_group_type_remove_ptr_ex(
 +        struct Main *bmain, struct wmManipulatorGroupType *wgt,
 +        struct wmManipulatorMapType *mmap_type);
 +void WM_manipulator_group_type_remove_ptr(
 +        struct Main *bmain, struct wmManipulatorGroupType *wgt);
 +void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname);
 +
 +void WM_manipulator_group_type_unlink_delayed_ptr_ex(
 +        struct wmManipulatorGroupType *wgt,
 +        struct wmManipulatorMapType *mmap_type);
 +void WM_manipulator_group_type_unlink_delayed_ptr(
 +        struct wmManipulatorGroupType *wgt);
 +void WM_manipulator_group_type_unlink_delayed(const char *idname);
 +
 +/* Utilities */
 +bool WM_manipulator_context_check_drawstep(const struct bContext *C, eWM_ManipulatorMapDrawStep step);
 +
 +#endif  /* __WM_MANIPULATOR_API_H__ */
index 354fd32cbd6f287ccd36700d5090bf97f82336a0,0000000000000000000000000000000000000000..0e63f3d6ffe41978fceb2b4fa5015f961e593c1a
mode 100644,000000..100644
--- /dev/null
@@@ -1,925 -1,0 +1,925 @@@
-       WM_manipulatormap_tooltip_clear(C, mmap);
 +/*
 + * ***** 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_group.c
 + *  \ingroup wm
 + *
 + * \name Manipulator-Group
 + *
 + * Manipulator-groups store and manage groups of manipulators. They can be
 + * attached to modal handlers and have own keymaps.
 + */
 +
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_listbase.h"
 +#include "BLI_string.h"
 +#include "BLI_math.h"
 +
 +#include "BKE_context.h"
 +#include "BKE_main.h"
 +#include "BKE_report.h"
 +
 +#include "RNA_access.h"
 +#include "RNA_define.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +#include "wm_event_system.h"
 +
 +#include "ED_screen.h"
 +
 +/* own includes */
 +#include "wm_manipulator_wmapi.h"
 +#include "wm_manipulator_intern.h"
 +
 +#ifdef WITH_PYTHON
 +#  include "BPY_extern.h"
 +#endif
 +
 +/* Allow manipulator part's to be single click only,
 + * dragging falls back to activating their 'drag_part' action. */
 +#define USE_DRAG_DETECT
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmManipulatorGroup
 + *
 + * \{ */
 +
 +/**
 + * Create a new manipulator-group from \a wgt.
 + */
 +wmManipulatorGroup *wm_manipulatorgroup_new_from_type(
 +        wmManipulatorMap *mmap, wmManipulatorGroupType *wgt)
 +{
 +      wmManipulatorGroup *mgroup = MEM_callocN(sizeof(*mgroup), "manipulator-group");
 +      mgroup->type = wgt;
 +
 +      /* keep back-link */
 +      mgroup->parent_mmap = mmap;
 +
 +      BLI_addtail(&mmap->groups, mgroup);
 +
 +      return mgroup;
 +}
 +
 +void wm_manipulatorgroup_free(bContext *C, wmManipulatorGroup *mgroup)
 +{
 +      wmManipulatorMap *mmap = mgroup->parent_mmap;
 +
 +      /* Similar to WM_manipulator_unlink, but only to keep mmap state correct,
 +       * we don't want to run callbacks. */
 +      if (mmap->mmap_context.highlight && mmap->mmap_context.highlight->parent_mgroup == mgroup) {
 +              wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
 +      }
 +      if (mmap->mmap_context.modal && mmap->mmap_context.modal->parent_mgroup == mgroup) {
 +              wm_manipulatormap_modal_set(mmap, C, mmap->mmap_context.modal, NULL, false);
 +      }
 +
 +      for (wmManipulator *mpr = mgroup->manipulators.first, *mpr_next; mpr; mpr = mpr_next) {
 +              mpr_next = mpr->next;
 +              if (mmap->mmap_context.select.len) {
 +                      WM_manipulator_select_unlink(mmap, mpr);
 +              }
 +              WM_manipulator_free(mpr);
 +      }
 +      BLI_listbase_clear(&mgroup->manipulators);
 +
 +#ifdef WITH_PYTHON
 +      if (mgroup->py_instance) {
 +              /* do this first in case there are any __del__ functions or
 +               * similar that use properties */
 +              BPY_DECREF_RNA_INVALIDATE(mgroup->py_instance);
 +      }
 +#endif
 +
 +      if (mgroup->reports && (mgroup->reports->flag & RPT_FREE)) {
 +              BKE_reports_clear(mgroup->reports);
 +              MEM_freeN(mgroup->reports);
 +      }
 +
 +      if (mgroup->customdata_free) {
 +              mgroup->customdata_free(mgroup->customdata);
 +      }
 +      else {
 +              MEM_SAFE_FREE(mgroup->customdata);
 +      }
 +
 +      BLI_remlink(&mmap->groups, mgroup);
 +
 +      MEM_freeN(mgroup);
 +}
 +
 +/**
 + * Add \a manipulator to \a mgroup and make sure its name is unique within the group.
 + */
 +void wm_manipulatorgroup_manipulator_register(wmManipulatorGroup *mgroup, wmManipulator *mpr)
 +{
 +      BLI_assert(BLI_findindex(&mgroup->manipulators, mpr) == -1);
 +      BLI_addtail(&mgroup->manipulators, mpr);
 +      mpr->parent_mgroup = mgroup;
 +}
 +
 +wmManipulator *wm_manipulatorgroup_find_intersected_manipulator(
 +        const wmManipulatorGroup *mgroup, bContext *C, const wmEvent *event,
 +        int *r_part)
 +{
 +      for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
 +              if (mpr->type->test_select && (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) {
 +                      if ((*r_part = mpr->type->test_select(C, mpr, event)) != -1) {
 +                              return mpr;
 +                      }
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +/**
 + * Adds all manipulators of \a mgroup that can be selected to the head of \a listbase. Added items need freeing!
 + */
 +void wm_manipulatorgroup_intersectable_manipulators_to_list(const wmManipulatorGroup *mgroup, ListBase *listbase)
 +{
 +      for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
 +              if ((mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) {
 +                      if (((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) && mpr->type->draw_select) ||
 +                          ((mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0 && mpr->type->test_select))
 +                      {
 +                              BLI_addhead(listbase, BLI_genericNodeN(mpr));
 +                      }
 +              }
 +      }
 +}
 +
 +void wm_manipulatorgroup_ensure_initialized(wmManipulatorGroup *mgroup, const bContext *C)
 +{
 +      /* prepare for first draw */
 +      if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0)) {
 +              mgroup->type->setup(C, mgroup);
 +
 +              /* Not ideal, initialize keymap here, needed for RNA runtime generated manipulators. */
 +              wmManipulatorGroupType *wgt = mgroup->type;
 +              if (wgt->keymap == NULL) {
 +                      wmWindowManager *wm = CTX_wm_manager(C);
 +                      wm_manipulatorgrouptype_setup_keymap(wgt, wm->defaultconf);
 +                      BLI_assert(wgt->keymap != NULL);
 +              }
 +              mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_SETUP;
 +      }
 +
 +      /* refresh may be called multiple times, this just ensures its called at least once before we draw. */
 +      if (UNLIKELY((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_REFRESH) == 0)) {
 +              if (mgroup->type->refresh) {
 +                      mgroup->type->refresh(C, mgroup);
 +              }
 +              mgroup->init_flag |= WM_MANIPULATORGROUP_INIT_REFRESH;
 +      }
 +}
 +
 +bool wm_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->poll || mgroup->type->poll(C, mgroup->type));
 +}
 +
 +bool wm_manipulatorgroup_is_visible_in_drawstep(
 +        const wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep)
 +{
 +      switch (drawstep) {
 +              case WM_MANIPULATORMAP_DRAWSTEP_2D:
 +                      return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) == 0;
 +              case WM_MANIPULATORMAP_DRAWSTEP_3D:
 +                      return (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D);
 +              default:
 +                      BLI_assert(0);
 +                      return false;
 +      }
 +}
 +
 +bool wm_manipulatorgroup_is_any_selected(const wmManipulatorGroup *mgroup)
 +{
 +      if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT) {
 +              for (const wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
 +                      if (mpr->state & WM_MANIPULATOR_STATE_SELECT) {
 +                              return true;
 +                      }
 +              }
 +      }
 +      return false;
 +}
 +
 +/** \} */
 +
 +/** \name Manipulator operators
 + *
 + * Basic operators for manipulator interaction with user configurable keymaps.
 + *
 + * \{ */
 +
 +static int manipulator_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
 +{
 +      ARegion *ar = CTX_wm_region(C);
 +      wmManipulatorMap *mmap = ar->manipulator_map;
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +      wmManipulator *highlight = mmap->mmap_context.highlight;
 +
 +      bool extend = RNA_boolean_get(op->ptr, "extend");
 +      bool deselect = RNA_boolean_get(op->ptr, "deselect");
 +      bool toggle = RNA_boolean_get(op->ptr, "toggle");
 +
 +      /* deselect all first */
 +      if (extend == false && deselect == false && toggle == false) {
 +              wm_manipulatormap_deselect_all(mmap);
 +              BLI_assert(msel->items == NULL && msel->len == 0);
 +              UNUSED_VARS_NDEBUG(msel);
 +      }
 +
 +      if (highlight) {
 +              const bool is_selected = (highlight->state & WM_MANIPULATOR_STATE_SELECT);
 +              bool redraw = false;
 +
 +              if (toggle) {
 +                      /* toggle: deselect if already selected, else select */
 +                      deselect = is_selected;
 +              }
 +
 +              if (deselect) {
 +                      if (is_selected && WM_manipulator_select_set(mmap, highlight, false)) {
 +                              redraw = true;
 +                      }
 +              }
 +              else if (wm_manipulator_select_and_highlight(C, mmap, highlight)) {
 +                      redraw = true;
 +              }
 +
 +              if (redraw) {
 +                      ED_region_tag_redraw(ar);
 +              }
 +
 +              return OPERATOR_FINISHED;
 +      }
 +      else {
 +              BLI_assert(0);
 +              return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
 +      }
 +
 +      return OPERATOR_PASS_THROUGH;
 +}
 +
 +void MANIPULATORGROUP_OT_manipulator_select(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "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;
 +      wmManipulatorGroup *mgroup;
 +      wmManipulator *mpr_modal;
 +
 +      int init_event; /* initial event type */
 +      int flag;       /* tweak flags */
 +
 +#ifdef USE_DRAG_DETECT
 +      /* True until the mouse is moved (only use when the operator has no modal).
 +       * this allows some manipulators to be click-only. */
 +      enum {
 +              /* Don't detect dragging. */
 +              DRAG_NOP = 0,
 +              /* Detect dragging (wait until a drag or click is detected). */
 +              DRAG_DETECT,
 +              /* Drag has started, idle until there is no active modal operator.
 +               * This is needed because finishing the modal operator also exits
 +               * the modal manipulator state (un-grabbs the cursor).
 +               * Ideally this workaround could be removed later. */
 +              DRAG_IDLE,
 +      } drag_state;
 +#endif
 +
 +} ManipulatorTweakData;
 +
 +static bool manipulator_tweak_start(
 +        bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr, const wmEvent *event)
 +{
 +      /* activate highlighted manipulator */
 +      wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
 +
 +      return (mpr->state & WM_MANIPULATOR_STATE_MODAL);
 +}
 +
 +static bool manipulator_tweak_start_and_finish(
 +        bContext *C, wmManipulatorMap *mmap, wmManipulator *mpr, const wmEvent *event, bool *r_is_modal)
 +{
 +      wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
 +      if (r_is_modal) {
 +              *r_is_modal = false;
 +      }
 +      if (mpop && mpop->type) {
 +              /* XXX temporary workaround for modal manipulator operator
 +               * conflicting with modal operator attached to manipulator */
 +              if (mpop->type->modal) {
 +                      /* activate highlighted manipulator */
 +                      wm_manipulatormap_modal_set(mmap, C, mpr, event, true);
 +                      if (r_is_modal) {
 +                              *r_is_modal = true;
 +                      }
 +              }
 +              else {
 +                      /* Allow for 'button' manipulators, single click to run an action. */
 +                      WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr);
 +              }
 +              return true;
 +      }
 +      else {
 +              return false;
 +      }
 +}
 +
 +static void manipulator_tweak_finish(bContext *C, wmOperator *op, const bool cancel, bool clear_modal)
 +{
 +      ManipulatorTweakData *mtweak = op->customdata;
 +      if (mtweak->mpr_modal->type->exit) {
 +              mtweak->mpr_modal->type->exit(C, mtweak->mpr_modal, cancel);
 +      }
 +      if (clear_modal) {
 +              /* The manipulator may have been removed. */
 +              if ((BLI_findindex(&mtweak->mmap->groups, mtweak->mgroup) != -1) &&
 +                  (BLI_findindex(&mtweak->mgroup->manipulators, mtweak->mpr_modal) != -1))
 +              {
 +                      wm_manipulatormap_modal_set(mtweak->mmap, C, mtweak->mpr_modal, NULL, false);
 +              }
 +      }
 +      MEM_freeN(mtweak);
 +}
 +
 +static int manipulator_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event)
 +{
 +      ManipulatorTweakData *mtweak = op->customdata;
 +      wmManipulator *mpr = mtweak->mpr_modal;
 +      int retval = OPERATOR_PASS_THROUGH;
 +      bool clear_modal = true;
 +
 +      if (mpr == NULL) {
 +              BLI_assert(0);
 +              return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
 +      }
 +
 +#ifdef USE_DRAG_DETECT
 +      wmManipulatorMap *mmap = mtweak->mmap;
 +      if (mtweak->drag_state == DRAG_DETECT) {
 +              if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
 +                      if (len_manhattan_v2v2_int(&event->x, mmap->mmap_context.event_xy) > 2) {
 +                              mtweak->drag_state = DRAG_IDLE;
 +                              mpr->highlight_part = mpr->drag_part;
 +                      }
 +              }
 +              else if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
 +                      mtweak->drag_state = DRAG_NOP;
 +                      retval = OPERATOR_FINISHED;
 +              }
 +
 +              if (mtweak->drag_state != DRAG_DETECT) {
 +                      /* Follow logic in 'manipulator_tweak_invoke' */
 +                      bool is_modal = false;
 +                      if (manipulator_tweak_start_and_finish(C, mmap, mpr, event, &is_modal)) {
 +                              if (is_modal) {
 +                                      clear_modal = false;
 +                              }
 +                      }
 +                      else {
 +                              if (!manipulator_tweak_start(C, mmap, mpr, event)) {
 +                                      retval = OPERATOR_FINISHED;
 +                              }
 +                      }
 +              }
 +      }
 +      if (mtweak->drag_state == DRAG_IDLE) {
 +              if (mmap->mmap_context.modal != NULL) {
 +                      return OPERATOR_PASS_THROUGH;
 +              }
 +              else {
 +                      manipulator_tweak_finish(C, op, false, false);
 +                      return OPERATOR_FINISHED;
 +              }
 +      }
 +#endif  /* USE_DRAG_DETECT */
 +
 +      if (retval == OPERATOR_FINISHED) {
 +              /* pass */
 +      }
 +      else if (event->type == mtweak->init_event && event->val == KM_RELEASE) {
 +              retval = OPERATOR_FINISHED;
 +      }
 +      else if (event->type == EVT_MODAL_MAP) {
 +              switch (event->val) {
 +                      case TWEAK_MODAL_CANCEL:
 +                              retval = OPERATOR_CANCELLED;
 +                              break;
 +                      case TWEAK_MODAL_CONFIRM:
 +                              retval = OPERATOR_FINISHED;
 +                              break;
 +                      case TWEAK_MODAL_PRECISION_ON:
 +                              mtweak->flag |= WM_MANIPULATOR_TWEAK_PRECISE;
 +                              break;
 +                      case TWEAK_MODAL_PRECISION_OFF:
 +                              mtweak->flag &= ~WM_MANIPULATOR_TWEAK_PRECISE;
 +                              break;
 +
 +                      case TWEAK_MODAL_SNAP_ON:
 +                              mtweak->flag |= WM_MANIPULATOR_TWEAK_SNAP;
 +                              break;
 +                      case TWEAK_MODAL_SNAP_OFF:
 +                              mtweak->flag &= ~WM_MANIPULATOR_TWEAK_SNAP;
 +                              break;
 +              }
 +      }
 +
 +      if (retval != OPERATOR_PASS_THROUGH) {
 +              manipulator_tweak_finish(C, op, retval != OPERATOR_FINISHED, clear_modal);
 +              return retval;
 +      }
 +
 +      /* handle manipulator */
 +      wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal;
 +      if (modal_fn) {
 +              int modal_retval = modal_fn(C, mpr, event, mtweak->flag);
 +
 +              if ((modal_retval & OPERATOR_RUNNING_MODAL) == 0) {
 +                      manipulator_tweak_finish(C, op, (modal_retval & OPERATOR_CANCELLED) != 0, true);
 +                      return OPERATOR_FINISHED;
 +              }
 +
 +              /* Ugly hack to send manipulator events */
 +              ((wmEvent *)event)->type = EVT_MANIPULATOR_UPDATE;
 +      }
 +
 +      /* always return PASS_THROUGH so modal handlers
 +       * with manipulators attached can update */
 +      BLI_assert(retval == OPERATOR_PASS_THROUGH);
 +      return OPERATOR_PASS_THROUGH;
 +}
 +
 +static int manipulator_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 +{
 +      ARegion *ar = CTX_wm_region(C);
 +      wmManipulatorMap *mmap = ar->manipulator_map;
 +      wmManipulator *mpr = mmap->mmap_context.highlight;
 +
 +      /* Needed for single click actions which don't enter modal state. */
++      WM_tooltip_clear(C, CTX_wm_window(C));
 +
 +      if (!mpr) {
 +              /* wm_handlers_do_intern shouldn't let this happen */
 +              BLI_assert(0);
 +              return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
 +      }
 +
 +      bool use_drag_fallback = false;
 +
 +#ifdef USE_DRAG_DETECT
 +      use_drag_fallback = !ELEM(mpr->drag_part, -1, mpr->highlight_part);
 +#endif
 +
 +      if (use_drag_fallback == false) {
 +              if (manipulator_tweak_start_and_finish(C, mmap, mpr, event, NULL)) {
 +                      return OPERATOR_FINISHED;
 +              }
 +      }
 +
 +      bool use_drag_detect = false;
 +#ifdef USE_DRAG_DETECT
 +      if (use_drag_fallback) {
 +              wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
 +              if (mpop && mpop->type) {
 +                      if (mpop->type->modal == NULL) {
 +                              use_drag_detect = true;
 +                      }
 +              }
 +      }
 +#endif
 +
 +      if (use_drag_detect == false) {
 +              if (!manipulator_tweak_start(C, mmap, mpr, event)) {
 +                      /* failed to start */
 +                      return OPERATOR_PASS_THROUGH;
 +              }
 +      }
 +
 +      ManipulatorTweakData *mtweak = MEM_mallocN(sizeof(ManipulatorTweakData), __func__);
 +
 +      mtweak->init_event = WM_userdef_event_type_from_keymap_type(event->type);
 +      mtweak->mpr_modal = mmap->mmap_context.highlight;
 +      mtweak->mgroup = mtweak->mpr_modal->parent_mgroup;
 +      mtweak->mmap = mmap;
 +      mtweak->flag = 0;
 +
 +#ifdef USE_DRAG_DETECT
 +      mtweak->drag_state = use_drag_detect ? DRAG_DETECT : DRAG_NOP;
 +#endif
 +
 +      op->customdata = mtweak;
 +
 +      WM_event_add_modal_handler(C, op);
 +
 +      return OPERATOR_RUNNING_MODAL;
 +}
 +
 +void MANIPULATORGROUP_OT_manipulator_tweak(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "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;
 +
 +      /* TODO(campbell) This causes problems tweaking settings for operators,
 +       * need to find a way to support this. */
 +#if 0
 +      ot->flag = OPTYPE_UNDO;
 +#endif
 +}
 +
 +/** \} */ // 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", ""},
 +              {TWEAK_MODAL_SNAP_ON, "SNAP_ON", 0, "Enable Snap", ""},
 +              {TWEAK_MODAL_SNAP_OFF, "SNAP_OFF", 0, "Disable Snap", ""},
 +              {0, NULL, 0, NULL, NULL}
 +      };
 +
 +
 +      BLI_snprintf(name, sizeof(name), "%s Tweak Modal Map", mgroupname);
 +      keymap = WM_modalkeymap_get(keyconf, name);
 +
 +      /* this function is called for each spacetype, only needs to add map once */
 +      if (keymap && keymap->modal_items)
 +              return NULL;
 +
 +      keymap = WM_modalkeymap_add(keyconf, name, modal_items);
 +
 +
 +      /* items for modal map */
 +      WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
 +      WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL);
 +
 +      WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
 +      WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM);
 +
 +      WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
 +      WM_modalkeymap_add_item(keymap, RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
 +      WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON);
 +      WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF);
 +
 +      WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
 +      WM_modalkeymap_add_item(keymap, RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
 +      WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON);
 +      WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF);
 +
 +      WM_modalkeymap_assign(keymap, "MANIPULATORGROUP_OT_manipulator_tweak");
 +
 +      return keymap;
 +}
 +
 +/**
 + * Common default keymap for manipulator groups
 + */
 +wmKeyMap *WM_manipulatorgroup_keymap_common(
 +        const wmManipulatorGroupType *wgt, wmKeyConfig *config)
 +{
 +      /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */
 +      wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid);
 +
 +      WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", LEFTMOUSE, KM_PRESS, KM_ANY, 0);
 +      manipulatorgroup_tweak_modal_keymap(config, wgt->name);
 +
 +      return km;
 +}
 +
 +/**
 + * Variation of #WM_manipulatorgroup_keymap_common but with keymap items for selection
 + */
 +wmKeyMap *WM_manipulatorgroup_keymap_common_select(
 +        const wmManipulatorGroupType *wgt, wmKeyConfig *config)
 +{
 +      /* Use area and region id since we might have multiple manipulators with the same name in different areas/regions */
 +      wmKeyMap *km = WM_keymap_find(config, wgt->name, wgt->mmap_params.spaceid, wgt->mmap_params.regionid);
 +
 +      WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0);
 +      WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_tweak", EVT_TWEAK_S, KM_ANY, 0, 0);
 +      manipulatorgroup_tweak_modal_keymap(config, wgt->name);
 +
 +      wmKeyMapItem *kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, 0, 0);
 +      RNA_boolean_set(kmi->ptr, "extend", false);
 +      RNA_boolean_set(kmi->ptr, "deselect", false);
 +      RNA_boolean_set(kmi->ptr, "toggle", false);
 +      kmi = WM_keymap_add_item(km, "MANIPULATORGROUP_OT_manipulator_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
 +      RNA_boolean_set(kmi->ptr, "extend", false);
 +      RNA_boolean_set(kmi->ptr, "deselect", false);
 +      RNA_boolean_set(kmi->ptr, "toggle", true);
 +
 +      return km;
 +}
 +
 +/** \} */ /* wmManipulatorGroup */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmManipulatorGroupType
 + *
 + * \{ */
 +
 +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find_ptr(
 +        struct wmManipulatorMapType *mmap_type,
 +        const wmManipulatorGroupType *wgt)
 +{
 +      /* could use hash lookups as operator types do, for now simple search. */
 +      for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
 +           wgt_ref;
 +           wgt_ref = wgt_ref->next)
 +      {
 +              if (wgt_ref->type == wgt) {
 +                      return wgt_ref;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +struct wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_find(
 +        struct wmManipulatorMapType *mmap_type,
 +        const char *idname)
 +{
 +      /* could use hash lookups as operator types do, for now simple search. */
 +      for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
 +           wgt_ref;
 +           wgt_ref = wgt_ref->next)
 +      {
 +              if (STREQ(idname, wgt_ref->type->idname)) {
 +                      return wgt_ref;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +/**
 + * Use this for registering manipulators on startup. For runtime, use #WM_manipulatormaptype_group_link_runtime.
 + */
 +wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link(
 +        wmManipulatorMapType *mmap_type, const char *idname)
 +{
 +      wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
 +      BLI_assert(wgt != NULL);
 +      return WM_manipulatormaptype_group_link_ptr(mmap_type, wgt);
 +}
 +
 +wmManipulatorGroupTypeRef *WM_manipulatormaptype_group_link_ptr(
 +        wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
 +{
 +      wmManipulatorGroupTypeRef *wgt_ref = MEM_callocN(sizeof(wmManipulatorGroupTypeRef), "manipulator-group-ref");
 +      wgt_ref->type = wgt;
 +      BLI_addtail(&mmap_type->grouptype_refs, wgt_ref);
 +      return wgt_ref;
 +}
 +
 +void WM_manipulatormaptype_group_init_runtime_keymap(
 +        const Main *bmain,
 +        wmManipulatorGroupType *wgt)
 +{
 +      /* init keymap - on startup there's an extra call to init keymaps for 'permanent' manipulator-groups */
 +      wm_manipulatorgrouptype_setup_keymap(wgt, ((wmWindowManager *)bmain->wm.first)->defaultconf);
 +}
 +
 +void WM_manipulatormaptype_group_init_runtime(
 +        const Main *bmain, wmManipulatorMapType *mmap_type,
 +        wmManipulatorGroupType *wgt)
 +{
 +      /* now create a manipulator for all existing areas */
 +      for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
 +              for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
 +                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                              ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
 +                              for (ARegion *ar = lb->first; ar; ar = ar->next) {
 +                                      wmManipulatorMap *mmap = ar->manipulator_map;
 +                                      if (mmap && mmap->type == mmap_type) {
 +                                              wm_manipulatorgroup_new_from_type(mmap, wgt);
 +
 +                                              /* just add here, drawing will occur on next update */
 +                                              wm_manipulatormap_highlight_set(mmap, NULL, NULL, 0);
 +                                              ED_region_tag_redraw(ar);
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +
 +/**
 + * Unlike #WM_manipulatormaptype_group_unlink this doesn't maintain correct state, simply free.
 + */
 +void WM_manipulatormaptype_group_free(wmManipulatorGroupTypeRef *wgt_ref)
 +{
 +      MEM_freeN(wgt_ref);
 +}
 +
 +void WM_manipulatormaptype_group_unlink(
 +        bContext *C, Main *bmain, wmManipulatorMapType *mmap_type,
 +        const wmManipulatorGroupType *wgt)
 +{
 +      /* Free instances. */
 +      for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
 +              for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
 +                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                              ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
 +                              for (ARegion *ar = lb->first; ar; ar = ar->next) {
 +                                      wmManipulatorMap *mmap = ar->manipulator_map;
 +                                      if (mmap && mmap->type == mmap_type) {
 +                                              wmManipulatorGroup *mgroup, *mgroup_next;
 +                                              for (mgroup = mmap->groups.first; mgroup; mgroup = mgroup_next) {
 +                                                      mgroup_next = mgroup->next;
 +                                                      if (mgroup->type == wgt) {
 +                                                              BLI_assert(mgroup->parent_mmap == mmap);
 +                                                              wm_manipulatorgroup_free(C, mgroup);
 +                                                              ED_region_tag_redraw(ar);
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      /* Free types. */
 +      wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt);
 +      if (wgt_ref) {
 +              BLI_remlink(&mmap_type->grouptype_refs, wgt_ref);
 +              WM_manipulatormaptype_group_free(wgt_ref);
 +      }
 +
 +      /* Note, we may want to keep this keymap for editing */
 +      WM_keymap_remove(wgt->keyconf, wgt->keymap);
 +
 +      BLI_assert(WM_manipulatormaptype_group_find_ptr(mmap_type, wgt) == NULL);
 +}
 +
 +void wm_manipulatorgrouptype_setup_keymap(
 +        wmManipulatorGroupType *wgt, wmKeyConfig *keyconf)
 +{
 +      /* Use flag since setup_keymap may return NULL,
 +       * in that case we better not keep calling it. */
 +      if (wgt->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) {
 +              wgt->keymap = wgt->setup_keymap(wgt, keyconf);
 +              wgt->keyconf = keyconf;
 +              wgt->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
 +      }
 +}
 +
 +/** \} */ /* wmManipulatorGroupType */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name High Level Add/Remove API
 + *
 + * For use directly from operators & RNA registration.
 + *
 + * \note In context of manipulator API these names are a bit misleading,
 + * but for general use terms its OK.
 + * `WM_manipulator_group_type_add` would be more correctly called:
 + * `WM_manipulatormaptype_grouptype_reference_link`
 + * but for general purpose API this is too detailed & annoying.
 + *
 + * \note We may want to return a value if there is nothing to remove.
 + *
 + * \{ */
 +
 +void WM_manipulator_group_type_add_ptr_ex(
 +        wmManipulatorGroupType *wgt,
 +        wmManipulatorMapType *mmap_type)
 +{
 +      WM_manipulatormaptype_group_link_ptr(mmap_type, wgt);
 +
 +      WM_manipulatorconfig_update_tag_init(mmap_type, wgt);
 +}
 +void WM_manipulator_group_type_add_ptr(
 +        wmManipulatorGroupType *wgt)
 +{
 +      wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
 +      WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type);
 +}
 +void WM_manipulator_group_type_add(const char *idname)
 +{
 +      wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
 +      BLI_assert(wgt != NULL);
 +      WM_manipulator_group_type_add_ptr(wgt);
 +}
 +
 +void WM_manipulator_group_type_ensure_ptr_ex(
 +        wmManipulatorGroupType *wgt,
 +        wmManipulatorMapType *mmap_type)
 +{
 +      wmManipulatorGroupTypeRef *wgt_ref = WM_manipulatormaptype_group_find_ptr(mmap_type, wgt);
 +      if (wgt_ref == NULL) {
 +              WM_manipulator_group_type_add_ptr_ex(wgt, mmap_type);
 +      }
 +}
 +void WM_manipulator_group_type_ensure_ptr(
 +        wmManipulatorGroupType *wgt)
 +{
 +      wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
 +      WM_manipulator_group_type_ensure_ptr_ex(wgt, mmap_type);
 +}
 +void WM_manipulator_group_type_ensure(const char *idname)
 +{
 +      wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
 +      BLI_assert(wgt != NULL);
 +      WM_manipulator_group_type_ensure_ptr(wgt);
 +}
 +
 +void WM_manipulator_group_type_remove_ptr_ex(
 +        struct Main *bmain, wmManipulatorGroupType *wgt,
 +        wmManipulatorMapType *mmap_type)
 +{
 +      WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt);
 +      WM_manipulatorgrouptype_free_ptr(wgt);
 +}
 +void WM_manipulator_group_type_remove_ptr(
 +        struct Main *bmain, wmManipulatorGroupType *wgt)
 +{
 +      wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
 +      WM_manipulator_group_type_remove_ptr_ex(bmain, wgt, mmap_type);
 +}
 +void WM_manipulator_group_type_remove(struct Main *bmain, const char *idname)
 +{
 +      wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
 +      BLI_assert(wgt != NULL);
 +      WM_manipulator_group_type_remove_ptr(bmain, wgt);
 +}
 +
 +/* delayed versions */
 +
 +void WM_manipulator_group_type_unlink_delayed_ptr_ex(
 +        wmManipulatorGroupType *wgt,
 +        wmManipulatorMapType *mmap_type)
 +{
 +      WM_manipulatorconfig_update_tag_remove(mmap_type, wgt);
 +}
 +
 +void WM_manipulator_group_type_unlink_delayed_ptr(
 +        wmManipulatorGroupType *wgt)
 +{
 +      wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(&wgt->mmap_params);
 +      WM_manipulator_group_type_unlink_delayed_ptr_ex(wgt, mmap_type);
 +}
 +
 +void WM_manipulator_group_type_unlink_delayed(const char *idname)
 +{
 +      wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
 +      BLI_assert(wgt != NULL);
 +      WM_manipulator_group_type_unlink_delayed_ptr(wgt);
 +}
 +
 +/** \} */
index ef3ccaaf47fb5f101b660d57998edbce0f595483,0000000000000000000000000000000000000000..b7982cf00df762474dc24e74126479a7a0b1ed84
mode 100644,000000..100644
--- /dev/null
@@@ -1,146 -1,0 +1,143 @@@
-               struct ARegion *tooltip;
-               struct wmTimer *tooltip_timer;
 +/*
 + * ***** 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;
 +struct ManipulatorGeomInfo;
 +struct GHashIterator;
 +
 +#include "wm_manipulator_fn.h"
 +
 +/* -------------------------------------------------------------------- */
 +/* wmManipulator */
 +
 +
 +bool wm_manipulator_select_set_ex(
 +        struct wmManipulatorMap *mmap, struct wmManipulator *mpr, bool select,
 +        bool use_array, bool use_callback);
 +bool wm_manipulator_select_and_highlight(bContext *C, struct wmManipulatorMap *mmap, struct wmManipulator *mpr);
 +
 +void wm_manipulator_calculate_scale(struct wmManipulator *mpr, const bContext *C);
 +void wm_manipulator_update(struct wmManipulator *mpr, const bContext *C, const bool refresh_map);
 +
 +int wm_manipulator_is_visible(struct wmManipulator *mpr);
 +enum {
 +      WM_MANIPULATOR_IS_VISIBLE_UPDATE = (1 << 0),
 +      WM_MANIPULATOR_IS_VISIBLE_DRAW = (1 << 1),
 +};
 +
 +/* -------------------------------------------------------------------- */
 +/* wmManipulatorGroup */
 +
 +enum {
 +      TWEAK_MODAL_CANCEL = 1,
 +      TWEAK_MODAL_CONFIRM,
 +      TWEAK_MODAL_PRECISION_ON,
 +      TWEAK_MODAL_PRECISION_OFF,
 +      TWEAK_MODAL_SNAP_ON,
 +      TWEAK_MODAL_SNAP_OFF,
 +};
 +
 +struct wmManipulatorGroup *wm_manipulatorgroup_new_from_type(
 +        struct wmManipulatorMap *mmap, struct wmManipulatorGroupType *wgt);
 +void wm_manipulatorgroup_free(bContext *C, struct wmManipulatorGroup *mgroup);
 +void wm_manipulatorgroup_manipulator_register(struct wmManipulatorGroup *mgroup, struct wmManipulator *mpr);
 +struct wmManipulator *wm_manipulatorgroup_find_intersected_manipulator(
 +        const struct wmManipulatorGroup *mgroup, struct bContext *C, const struct wmEvent *event,
 +        int *r_part);
 +void wm_manipulatorgroup_intersectable_manipulators_to_list(
 +        const struct wmManipulatorGroup *mgroup, struct ListBase *listbase);
 +void wm_manipulatorgroup_ensure_initialized(struct wmManipulatorGroup *mgroup, const struct bContext *C);
 +bool wm_manipulatorgroup_is_visible(const struct wmManipulatorGroup *mgroup, const struct bContext *C);
 +bool wm_manipulatorgroup_is_visible_in_drawstep(
 +        const struct wmManipulatorGroup *mgroup, const eWM_ManipulatorMapDrawStep drawstep);
 +
 +void wm_manipulatorgrouptype_setup_keymap(
 +        struct wmManipulatorGroupType *wgt, struct wmKeyConfig *keyconf);
 +
 +
 +/* -------------------------------------------------------------------- */
 +/* wmManipulatorMap */
 +
 +typedef struct wmManipulatorMapSelectState {
 +      struct wmManipulator **items;
 +      int len, len_alloc;
 +} wmManipulatorMapSelectState;
 +
 +struct wmManipulatorMap {
 +
 +      struct wmManipulatorMapType *type;
 +      ListBase groups;  /* wmManipulatorGroup */
 +
 +      /* private, update tagging (enum defined in C source). */
 +      char update_flag[WM_MANIPULATORMAP_DRAWSTEP_MAX];
 +
 +      /**
 +       * \brief Manipulator map runtime context
 +       *
 +       * Contains information about this manipulator-map. Currently
 +       * highlighted manipulator, currently selected manipulators, ...
 +       */
 +      struct {
 +              /* we redraw the manipulator-map when this changes */
 +              struct wmManipulator *highlight;
 +              /* User has clicked this manipulator and it gets all input. */
 +              struct wmManipulator *modal;
 +              /* array for all selected manipulators */
 +              struct wmManipulatorMapSelectState select;
 +              /* cursor location at point of entering modal (see: WM_MANIPULATOR_GRAB_CURSOR) */
 +              int event_xy[2];
 +              short event_grabcursor;
 +      } mmap_context;
 +};
 +
 +/**
 + * This is a container for all manipulator types that can be instantiated in a region.
 + * (similar to dropboxes).
 + *
 + * \note There is only ever one of these for every (area, region) combination.
 + */
 +struct wmManipulatorMapType {
 +      struct wmManipulatorMapType *next, *prev;
 +      short spaceid, regionid;
 +      /* types of manipulator-groups for this manipulator-map type */
 +      ListBase grouptype_refs;
 +
 +      /* eManipulatorMapTypeUpdateFlags */
 +      eWM_ManipulatorMapTypeUpdateFlag type_update_flag;
 +};
 +
 +void wm_manipulatormap_select_array_clear(struct wmManipulatorMap *mmap);
 +bool wm_manipulatormap_deselect_all(struct wmManipulatorMap *mmap);
 +void wm_manipulatormap_select_array_shrink(struct wmManipulatorMap *mmap, int len_subtract);
 +void wm_manipulatormap_select_array_push_back(struct wmManipulatorMap *mmap, wmManipulator *mpr);
 +void wm_manipulatormap_select_array_remove(struct wmManipulatorMap *mmap, wmManipulator *mpr);
 +
 +#endif
index 5bd717d3f50bfb21d3220c0a5daffe182e88124b,0000000000000000000000000000000000000000..a9875020fbbe9192e4a5e88945310eff70347385
mode 100644,000000..100644
--- /dev/null
@@@ -1,1224 -1,0 +1,1197 @@@
-               WM_manipulatormap_tooltip_clear(C, mmap);
 +/*
 + * ***** 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_map.c
 + *  \ingroup wm
 + */
 +
 +#include <string.h>
 +
 +#include "BLI_listbase.h"
 +#include "BLI_math.h"
 +#include "BLI_rect.h"
 +#include "BLI_string.h"
 +#include "BLI_ghash.h"
 +
 +#include "BKE_context.h"
 +#include "BKE_global.h"
 +
 +#include "ED_screen.h"
 +#include "ED_view3d.h"
 +
 +#include "GPU_glew.h"
 +#include "GPU_matrix.h"
 +#include "GPU_select.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +#include "wm_event_system.h"
 +
 +/* for tool-tips */
 +#include "UI_interface.h"
 +
 +#include "DEG_depsgraph.h"
 +
 +/* own includes */
 +#include "wm_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};
 +
 +/**
 + * Update when manipulator-map types change.
 + */
 +/* so operator removal can trigger update */
 +typedef enum eWM_ManipulatorGroupTypeGlobalFlag {
 +      WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0),
 +      WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1),
 +} eWM_ManipulatorGroupTypeGlobalFlag;
 +
 +static eWM_ManipulatorGroupTypeGlobalFlag wm_mmap_type_update_flag = 0;
 +
 +/**
 + * Manipulator-map update tagging.
 + */
 +enum {
 +      /** #manipulatormap_prepare_drawing has run */
 +      MANIPULATORMAP_IS_PREPARE_DRAW = (1 << 0),
 +      MANIPULATORMAP_IS_REFRESH_CALLBACK = (1 << 1),
 +};
 +
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmManipulatorMap Selection Array API
 + *
 + * Just handle ``wm_manipulatormap_select_array_*``, not flags or callbacks.
 + *
 + * \{ */
 +
 +static void wm_manipulatormap_select_array_ensure_len_alloc(wmManipulatorMap *mmap, int len)
 +{
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +      if (len <= msel->len_alloc) {
 +              return;
 +      }
 +      msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len);
 +      msel->len_alloc = len;
 +}
 +
 +void wm_manipulatormap_select_array_clear(wmManipulatorMap *mmap)
 +{
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +      MEM_SAFE_FREE(msel->items);
 +      msel->len = 0;
 +      msel->len_alloc = 0;
 +}
 +
 +void wm_manipulatormap_select_array_shrink(wmManipulatorMap *mmap, int len_subtract)
 +{
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +      msel->len -= len_subtract;
 +      if (msel->len <= 0) {
 +              wm_manipulatormap_select_array_clear(mmap);
 +      }
 +      else {
 +              if (msel->len < msel->len_alloc / 2) {
 +                      msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len);
 +                      msel->len_alloc = msel->len;
 +              }
 +      }
 +}
 +
 +void wm_manipulatormap_select_array_push_back(wmManipulatorMap *mmap, wmManipulator *mpr)
 +{
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +      BLI_assert(msel->len <= msel->len_alloc);
 +      if (msel->len == msel->len_alloc) {
 +              msel->len_alloc = (msel->len + 1) * 2;
 +              msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * msel->len_alloc);
 +      }
 +      msel->items[msel->len++] = mpr;
 +}
 +
 +void wm_manipulatormap_select_array_remove(wmManipulatorMap *mmap, wmManipulator *mpr)
 +{
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +      /* remove manipulator from selected_manipulators array */
 +      for (int i = 0; i < msel->len; i++) {
 +              if (msel->items[i] == mpr) {
 +                      for (int j = i; j < (msel->len - 1); j++) {
 +                              msel->items[j] = msel->items[j + 1];
 +                      }
 +                      wm_manipulatormap_select_array_shrink(mmap, 1);
 +                      break;
 +              }
 +      }
 +
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmManipulatorMap
 + *
 + * \{ */
 +
 +/**
 + * Creates a manipulator-map with all registered manipulators for that type
 + */
 +wmManipulatorMap *WM_manipulatormap_new_from_type(
 +        const struct wmManipulatorMapType_Params *mmap_params)
 +{
 +      wmManipulatorMapType *mmap_type = WM_manipulatormaptype_ensure(mmap_params);
 +      wmManipulatorMap *mmap;
 +
 +      mmap = MEM_callocN(sizeof(wmManipulatorMap), "ManipulatorMap");
 +      mmap->type = mmap_type;
 +      WM_manipulatormap_tag_refresh(mmap);
 +
 +      /* create all manipulator-groups for this manipulator-map. We may create an empty one
 +       * too in anticipation of manipulators from operators etc */
 +      for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) {
 +              wm_manipulatorgroup_new_from_type(mmap, wgt_ref->type);
 +      }
 +
 +      return mmap;
 +}
 +
 +void wm_manipulatormap_remove(wmManipulatorMap *mmap)
 +{
 +      /* Clear first so further calls don't waste time trying to maintain correct array state. */
 +      wm_manipulatormap_select_array_clear(mmap);
 +
 +      for (wmManipulatorGroup *mgroup = mmap->groups.first, *mgroup_next; mgroup; mgroup = mgroup_next) {
 +              mgroup_next = mgroup->next;
 +              BLI_assert(mgroup->parent_mmap == mmap);
 +              wm_manipulatorgroup_free(NULL, mgroup);
 +      }
 +      BLI_assert(BLI_listbase_is_empty(&mmap->groups));
 +
 +      MEM_freeN(mmap);
 +}
 +
 +
 +wmManipulatorGroup *WM_manipulatormap_group_find(
 +        struct wmManipulatorMap *mmap,
 +        const char *idname)
 +{
 +      wmManipulatorGroupType *wgt = WM_manipulatorgrouptype_find(idname, false);
 +      if (wgt) {
 +              return WM_manipulatormap_group_find_ptr(mmap, wgt);
 +      }
 +      return NULL;
 +}
 +
 +wmManipulatorGroup *WM_manipulatormap_group_find_ptr(
 +        struct wmManipulatorMap *mmap,
 +        const struct wmManipulatorGroupType *wgt)
 +{
 +      for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
 +              if (mgroup->type == wgt) {
 +                      return mgroup;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +const ListBase *WM_manipulatormap_group_list(wmManipulatorMap *mmap)
 +{
 +      return &mmap->groups;
 +}
 +
 +bool WM_manipulatormap_is_any_selected(const wmManipulatorMap *mmap)
 +{
 +      return mmap->mmap_context.select.len != 0;
 +}
 +
 +/**
 + * \note We could use a callback to define bounds, for now just use matrix location.
 + */
 +bool WM_manipulatormap_minmax(
 +        const wmManipulatorMap *mmap, bool UNUSED(use_hidden), bool use_select,
 +        float r_min[3], float r_max[3])
 +{
 +      if (use_select) {
 +              int i;
 +              for (i = 0; i < mmap->mmap_context.select.len; i++) {
 +                      minmax_v3v3_v3(r_min, r_max, mmap->mmap_context.select.items[i]->matrix_basis[3]);
 +              }
 +              return i != 0;
 +      }
 +      else {
 +              bool ok = false;
 +              BLI_assert(!"TODO");
 +              return ok;
 +      }
 +}
 +
 +/**
 + * Creates and returns idname hash table for (visible) manipulators in \a mmap
 + *
 + * \param poll  Polling function for excluding manipulators.
 + * \param data  Custom data passed to \a poll
 + *
 + * TODO(campbell): this uses unreliable order,
 + * best we use an iterator function instead of a hash.
 + */
 +static GHash *WM_manipulatormap_manipulator_hash_new(
 +        const bContext *C, wmManipulatorMap *mmap,
 +        bool (*poll)(const wmManipulator *, void *),
 +        void *data, const bool include_hidden)
 +{
 +      GHash *hash = BLI_ghash_ptr_new(__func__);
 +
 +      /* collect manipulators */
 +      for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
 +              if (!mgroup->type->poll || mgroup->type->poll(C, mgroup->type)) {
 +                      for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
 +                              if ((include_hidden || (mpr->flag & WM_MANIPULATOR_HIDDEN) == 0) &&
 +                                  (!poll || poll(mpr, data)))
 +                              {
 +                                      BLI_ghash_insert(hash, mpr, mpr);
 +                              }
 +                      }
 +              }
 +      }
 +
 +      return hash;
 +}
 +
 +void WM_manipulatormap_tag_refresh(wmManipulatorMap *mmap)
 +{
 +      if (mmap) {
 +              /* We might want only to refresh some, for tag all steps. */
 +              for (int i = 0; i < WM_MANIPULATORMAP_DRAWSTEP_MAX; i++) {
 +                      mmap->update_flag[i] |= (
 +                              MANIPULATORMAP_IS_PREPARE_DRAW |
 +                              MANIPULATORMAP_IS_REFRESH_CALLBACK);
 +              }
 +      }
 +}
 +
 +static bool manipulator_prepare_drawing(
 +        wmManipulatorMap *mmap, wmManipulator *mpr,
 +        const bContext *C, ListBase *draw_manipulators,
 +        const eWM_ManipulatorMapDrawStep drawstep)
 +{
 +      int do_draw = wm_manipulator_is_visible(mpr);
 +      if (do_draw == 0) {
 +              /* skip */
 +      }
 +      else {
 +              /* Ensure we get RNA updates */
 +              if (do_draw & WM_MANIPULATOR_IS_VISIBLE_UPDATE) {
 +                      /* hover manipulators need updating, even if we don't draw them */
 +                      wm_manipulator_update(mpr, C, (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_PREPARE_DRAW) != 0);
 +              }
 +              if (do_draw & WM_MANIPULATOR_IS_VISIBLE_DRAW) {
 +                      BLI_addhead(draw_manipulators, BLI_genericNodeN(mpr));
 +              }
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +/**
 + * Update manipulators of \a mmap to prepare for drawing. Adds all manipulators that
 + * should be drawn to list \a draw_manipulators, note that added items need freeing.
 + */
 +static void manipulatormap_prepare_drawing(
 +        wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators,
 +        const eWM_ManipulatorMapDrawStep drawstep)
 +{
 +      if (!mmap || BLI_listbase_is_empty(&mmap->groups))
 +              return;
 +      wmManipulator *mpr_modal = mmap->mmap_context.modal;
 +
 +      /* only active manipulator needs updating */
 +      if (mpr_modal) {
 +              if ((mpr_modal->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL) == 0) {
 +                      if (wm_manipulatorgroup_is_visible_in_drawstep(mpr_modal->parent_mgroup, drawstep)) {
 +                              if (manipulator_prepare_drawing(mmap, mpr_modal, C, draw_manipulators, drawstep)) {
 +                                      mmap->update_flag[drawstep] &= ~MANIPULATORMAP_IS_PREPARE_DRAW;
 +                              }
 +                      }
 +                      /* don't draw any other manipulators */
 +                      return;
 +              }
 +      }
 +
 +      for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
 +              /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */
 +              if (!wm_manipulatorgroup_is_visible_in_drawstep(mgroup, drawstep) ||
 +                  !wm_manipulatorgroup_is_visible(mgroup, C))
 +              {
 +                      continue;
 +              }
 +
 +              /* needs to be initialized on first draw */
 +              /* XXX weak: Manipulator-group may skip refreshing if it's invisible (map gets untagged nevertheless) */
 +              if (mmap->update_flag[drawstep] & MANIPULATORMAP_IS_REFRESH_CALLBACK) {
 +                      /* force refresh again. */
 +                      mgroup->init_flag &= ~WM_MANIPULATORGROUP_INIT_REFRESH;
 +              }
 +              /* Calls `setup`, `setup_keymap` and `refresh` if they're defined. */
 +              wm_manipulatorgroup_ensure_initialized(mgroup, C);
 +
 +              /* prepare drawing */
 +              if (mgroup->type->draw_prepare) {
 +                      mgroup->type->draw_prepare(C, mgroup);
 +              }
 +
 +              for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
 +                      manipulator_prepare_drawing(mmap, mpr, C, draw_manipulators, drawstep);
 +              }
 +      }
 +
 +      mmap->update_flag[drawstep] &=
 +              ~(MANIPULATORMAP_IS_REFRESH_CALLBACK |
 +                MANIPULATORMAP_IS_PREPARE_DRAW);
 +}
 +
 +/**
 + * Draw all visible manipulators in \a mmap.
 + * Uses global draw_manipulators listbase.
 + */
 +static void manipulators_draw_list(const wmManipulatorMap *mmap, const bContext *C, ListBase *draw_manipulators)
 +{
 +      /* Can be empty if we're dynamically added and removed. */
 +      if ((mmap == NULL) || BLI_listbase_is_empty(&mmap->groups)) {
 +              return;
 +      }
 +
 +      const bool draw_multisample = (U.ogl_multisamples != USER_MULTISAMPLE_NONE);
 +
 +      /* TODO this will need it own shader probably? don't think it can be handled from that point though. */
 +/*    const bool use_lighting = (U.manipulator_flag & V3D_MANIPULATOR_SHADED) != 0; */
 +
 +      /* enable multisampling */
 +      if (draw_multisample) {
 +              glEnable(GL_MULTISAMPLE);
 +      }
 +
 +      bool is_depth_prev = false;
 +
 +      /* draw_manipulators contains all visible manipulators - draw them */
 +      for (LinkData *link = draw_manipulators->first, *link_next; link; link = link_next) {
 +              wmManipulator *mpr = link->data;
 +              link_next = link->next;
 +
 +              bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0;
 +
 +              /* Weak! since we don't 100% support depth yet (select ignores depth) always show highlighted */
 +              if (is_depth && (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT)) {
 +                      is_depth = false;
 +              }
 +
 +              if (is_depth == is_depth_prev) {
 +                      /* pass */
 +              }
 +              else {
 +                      if (is_depth) {
 +                              glEnable(GL_DEPTH_TEST);
 +                      }
 +                      else {
 +                              glDisable(GL_DEPTH_TEST);
 +                      }
 +                      is_depth_prev = is_depth;
 +              }
 +
 +              mpr->type->draw(C, mpr);
 +              /* free/remove manipulator link after drawing */
 +              BLI_freelinkN(draw_manipulators, link);
 +      }
 +
 +      if (is_depth_prev) {
 +              glDisable(GL_DEPTH_TEST);
 +      }
 +
 +      if (draw_multisample) {
 +              glDisable(GL_MULTISAMPLE);
 +      }
 +}
 +
 +void WM_manipulatormap_draw(
 +        wmManipulatorMap *mmap, const bContext *C,
 +        const eWM_ManipulatorMapDrawStep drawstep)
 +{
 +      if (!WM_manipulator_context_check_drawstep(C, drawstep)) {
 +              return;
 +      }
 +
 +      ListBase draw_manipulators = {NULL};
 +
 +      manipulatormap_prepare_drawing(mmap, C, &draw_manipulators, drawstep);
 +      manipulators_draw_list(mmap, C, &draw_manipulators);
 +      BLI_assert(BLI_listbase_is_empty(&draw_manipulators));
 +}
 +
 +static void manipulator_draw_select_3D_loop(const bContext *C, ListBase *visible_manipulators)
 +{
 +      int select_id = 0;
 +      wmManipulator *mpr;
 +
 +      /* TODO(campbell): this depends on depth buffer being written to, currently broken for the 3D view. */
 +      bool is_depth_prev = false;
 +
 +      for (LinkData *link = visible_manipulators->first; link; link = link->next) {
 +              mpr = link->data;
 +
 +              bool is_depth = (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_DEPTH_3D) != 0;
 +              if (is_depth == is_depth_prev) {
 +                      /* pass */
 +              }
 +              else {
 +                      if (is_depth) {
 +                              glEnable(GL_DEPTH_TEST);
 +                      }
 +                      else {
 +                              glDisable(GL_DEPTH_TEST);
 +                      }
 +                      is_depth_prev = is_depth;
 +              }
 +
 +              /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected manipulator part id */
 +
 +              mpr->type->draw_select(C, mpr, select_id << 8);
 +
 +
 +              select_id++;
 +      }
 +
 +      if (is_depth_prev) {
 +              glDisable(GL_DEPTH_TEST);
 +      }
 +}
 +
 +static int manipulator_find_intersected_3d_intern(
 +        ListBase *visible_manipulators, const bContext *C, const int co[2],
 +        const int hotspot)
 +{
 +      EvaluationContext eval_ctx;
 +      ScrArea *sa = CTX_wm_area(C);
 +      ARegion *ar = CTX_wm_region(C);
 +      View3D *v3d = sa->spacedata.first;
 +      rcti rect;
 +      /* Almost certainly overkill, but allow for many custom manipulators. */
 +      GLuint buffer[MAXPICKBUF];
 +      short hits;
 +      const bool do_passes = GPU_select_query_check_active();
 +
 +      BLI_rcti_init_pt_radius(&rect, co, hotspot);
 +
 +      CTX_data_eval_ctx(C, &eval_ctx);
 +
 +      ED_view3d_draw_setup_view(CTX_wm_window(C), &eval_ctx, CTX_data_scene(C), ar, v3d, NULL, NULL, &rect);
 +
 +      if (do_passes)
 +              GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
 +      else
 +              GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_ALL, 0);
 +      /* do the drawing */
 +      manipulator_draw_select_3D_loop(C, visible_manipulators);
 +
 +      hits = GPU_select_end();
 +
 +      if (do_passes && (hits > 0)) {
 +              GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
 +              manipulator_draw_select_3D_loop(C, visible_manipulators);
 +              GPU_select_end();
 +      }
 +
 +      ED_view3d_draw_setup_view(CTX_wm_window(C), &eval_ctx, CTX_data_scene(C), ar, v3d, NULL, NULL, NULL);
 +
 +      const GLuint *hit_near = GPU_select_buffer_near(buffer, hits);
 +
 +      return hit_near ? hit_near[3] : -1;
 +}
 +
 +/**
 + * Try to find a 3D manipulator at screen-space coordinate \a co. Uses OpenGL picking.
 + */
 +static wmManipulator *manipulator_find_intersected_3d(
 +        bContext *C, const int co[2], ListBase *visible_manipulators,
 +        int *r_part)
 +{
 +      wmManipulator *result = NULL;
 +      int hit = -1;
 +
 +      int hotspot_radii[] = {
 +              3 * U.pixelsize,
 +#if 0 /* We may want to enable when selection doesn't run on mousemove! */
 +              7 * U.pixelsize,
 +#endif
 +      };
 +
 +      *r_part = 0;
 +
 +      /* set up view matrices */
 +      view3d_operator_needs_opengl(C);
 +
 +      hit = -1;
 +
 +      for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) {
 +              hit = manipulator_find_intersected_3d_intern(visible_manipulators, C, co, hotspot_radii[i]);
 +              if (hit != -1) {
 +                      break;
 +              }
 +      }
 +
 +      if (hit != -1) {
 +              LinkData *link = BLI_findlink(visible_manipulators, hit >> 8);
 +              if (link != NULL) {
 +                      *r_part = hit & 255;
 +                      result = link->data;
 +              }
 +              else {
 +                      /* All manipulators should use selection ID they're given as part of the callback,
 +                       * if they don't it will attempt tp lookup non-existing index. */
 +                      BLI_assert(0);
 +              }
 +      }
 +
 +      return result;
 +}
 +
 +/**
 + * Try to find a manipulator under the mouse position. 2D intersections have priority over
 + * 3D ones (could check for smallest screen-space distance but not needed right now).
 + */
 +wmManipulator *wm_manipulatormap_highlight_find(
 +        wmManipulatorMap *mmap, bContext *C, const wmEvent *event,
 +        int *r_part)
 +{
 +      wmManipulator *mpr = NULL;
 +      ListBase visible_3d_manipulators = {NULL};
 +      bool do_step[WM_MANIPULATORMAP_DRAWSTEP_MAX];
 +
 +      for (int i = 0; i < ARRAY_SIZE(do_step); i++) {
 +              do_step[i] = WM_manipulator_context_check_drawstep(C, i);
 +      }
 +
 +      for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
 +
 +              /* If it were important we could initialize here,
 +               * but this only happens when events are handled before drawing,
 +               * just skip to keep code-path for initializing manipulators simple. */
 +              if ((mgroup->init_flag & WM_MANIPULATORGROUP_INIT_SETUP) == 0) {
 +                      continue;
 +              }
 +
 +              if (wm_manipulatorgroup_is_visible(mgroup, C)) {
 +                      eWM_ManipulatorMapDrawStep step;
 +                      if (mgroup->type->flag & WM_MANIPULATORGROUPTYPE_3D) {
 +                              step = WM_MANIPULATORMAP_DRAWSTEP_3D;
 +                      }
 +                      else {
 +                              step = WM_MANIPULATORMAP_DRAWSTEP_2D;
 +                      }
 +
 +                      if (do_step[step]) {
 +                              if ((mmap->update_flag[step] & MANIPULATORMAP_IS_REFRESH_CALLBACK) &&
 +                                      (mgroup->type->refresh != NULL))
 +                              {
 +                                      mgroup->type->refresh(C, mgroup);
 +                                      /* cleared below */
 +                              }
 +                              if (step == WM_MANIPULATORMAP_DRAWSTEP_3D) {
 +                                      wm_manipulatorgroup_intersectable_manipulators_to_list(mgroup, &visible_3d_manipulators);
 +                              }
 +                              else if (step == WM_MANIPULATORMAP_DRAWSTEP_2D) {
 +                                      if ((mpr = wm_manipulatorgroup_find_intersected_manipulator(mgroup, C, event, r_part))) {
 +                                              break;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!BLI_listbase_is_empty(&visible_3d_manipulators)) {
 +              /* 2D manipulators get priority. */
 +              if (mpr == NULL) {
 +                      mpr = manipulator_find_intersected_3d(C, event->mval, &visible_3d_manipulators, r_part);
 +              }
 +              BLI_freelistN(&visible_3d_manipulators);
 +      }
 +
 +      mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_3D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK;
 +      mmap->update_flag[WM_MANIPULATORMAP_DRAWSTEP_2D] &= ~MANIPULATORMAP_IS_REFRESH_CALLBACK;
 +
 +      return mpr;
 +}
 +
 +void WM_manipulatormap_add_handlers(ARegion *ar, wmManipulatorMap *mmap)
 +{
 +      wmEventHandler *handler;
 +
 +      for (handler = ar->handlers.first; handler; handler = handler->next) {
 +              if (handler->manipulator_map == mmap) {
 +                      return;
 +              }
 +      }
 +
 +      handler = MEM_callocN(sizeof(wmEventHandler), "manipulator handler");
 +
 +      BLI_assert(mmap == ar->manipulator_map);
 +      handler->manipulator_map = mmap;
 +      BLI_addtail(&ar->handlers, handler);
 +}
 +
 +void wm_manipulatormaps_handled_modal_update(
 +        bContext *C, wmEvent *event, wmEventHandler *handler)
 +{
 +      const bool modal_running = (handler->op != NULL);
 +
 +      /* happens on render or when joining areas */
 +      if (!handler->op_region || !handler->op_region->manipulator_map) {
 +              return;
 +      }
 +
 +      wmManipulatorMap *mmap = handler->op_region->manipulator_map;
 +      wmManipulator *mpr = wm_manipulatormap_modal_get(mmap);
 +      ScrArea *area = CTX_wm_area(C);
 +      ARegion *region = CTX_wm_region(C);
 +
 +      wm_manipulatormap_handler_context(C, handler);
 +
 +      /* regular update for running operator */
 +      if (modal_running) {
 +              wmManipulatorOpElem *mpop = mpr ? WM_manipulator_operator_get(mpr, mpr->highlight_part) : NULL;
 +              if (mpr && mpop && (mpop->type != NULL) && (mpop->type == handler->op->type)) {
 +                      wmManipulatorFnModal modal_fn = mpr->custom_modal ? mpr->custom_modal : mpr->type->modal;
 +                      if (modal_fn != NULL) {
 +                              int retval = modal_fn(C, mpr, event, 0);
 +                              /* The manipulator is tried to the operator, we can't choose when to exit. */
 +                              BLI_assert(retval & OPERATOR_RUNNING_MODAL);
 +                              UNUSED_VARS_NDEBUG(retval);
 +                      }
 +              }
 +      }
 +      /* operator not running anymore */
 +      else {
 +              wm_manipulatormap_highlight_set(mmap, C, NULL, 0);
 +              if (mpr) {
 +                      /* This isn't defined if it ends because of success of cancel, we may want to change. */
 +                      bool cancel = true;
 +                      if (mpr->type->exit) {
 +                              mpr->type->exit(C, mpr, cancel);
 +                      }
 +                      wm_manipulatormap_modal_set(mmap, C, mpr, NULL, false);
 +              }
 +      }
 +
 +      /* restore the area */
 +      CTX_wm_area_set(C, area);
 +      CTX_wm_region_set(C, region);
 +}
 +
 +/**
 + * Deselect all selected manipulators in \a mmap.
 + * \return if selection has changed.
 + */
 +bool wm_manipulatormap_deselect_all(wmManipulatorMap *mmap)
 +{
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +
 +      if (msel->items == NULL || msel->len == 0) {
 +              return false;
 +      }
 +
 +      for (int i = 0; i < msel->len; i++) {
 +              wm_manipulator_select_set_ex(mmap, msel->items[i], false, false, true);
 +      }
 +
 +      wm_manipulatormap_select_array_clear(mmap);
 +
 +      /* always return true, we already checked
 +       * if there's anything to deselect */
 +      return true;
 +}
 +
 +BLI_INLINE bool manipulator_selectable_poll(const wmManipulator *mpr, void *UNUSED(data))
 +{
 +      return (mpr->parent_mgroup->type->flag & WM_MANIPULATORGROUPTYPE_SELECT);
 +}
 +
 +/**
 + * Select all selectable manipulators in \a mmap.
 + * \return if selection has changed.
 + */
 +static bool wm_manipulatormap_select_all_intern(
 +        bContext *C, wmManipulatorMap *mmap)
 +{
 +      wmManipulatorMapSelectState *msel = &mmap->mmap_context.select;
 +      /* GHash is used here to avoid having to loop over all manipulators twice (once to
 +       * get tot_sel for allocating, once for actually selecting). Instead we collect
 +       * selectable manipulators in hash table and use this to get tot_sel and do selection */
 +
 +      GHash *hash = WM_manipulatormap_manipulator_hash_new(C, mmap, manipulator_selectable_poll, NULL, true);
 +      GHashIterator gh_iter;
 +      int i;
 +      bool changed = false;
 +
 +      wm_manipulatormap_select_array_ensure_len_alloc(mmap, BLI_ghash_size(hash));
 +
 +      GHASH_ITER_INDEX (gh_iter, hash, i) {
 +              wmManipulator *mpr_iter = BLI_ghashIterator_getValue(&gh_iter);
 +              WM_manipulator_select_set(mmap, mpr_iter, true);
 +      }
 +      /* highlight first manipulator */
 +      wm_manipulatormap_highlight_set(mmap, C, msel->items[0], msel->items[0]->highlight_part);
 +
 +      BLI_assert(BLI_ghash_size(hash) == msel->len);
 +
 +      BLI_ghash_free(hash, NULL, NULL);
 +      return changed;
 +}
 +
 +/**
 + * Select/Deselect all selectable manipulators in \a mmap.
 + * \return if selection has changed.
 + *
 + * TODO select all by type
 + */
 +bool WM_manipulatormap_select_all(bContext *C, wmManipulatorMap *mmap, const int action)
 +{
 +      bool changed = false;
 +
 +      switch (action) {
 +              case SEL_SELECT:
 +                      changed = wm_manipulatormap_select_all_intern(C, mmap);
 +                      break;
 +              case SEL_DESELECT:
 +                      changed = wm_manipulatormap_deselect_all(mmap);
 +                      break;
 +              default:
 +                      BLI_assert(0);
 +                      break;
 +      }
 +
 +      if (changed)
 +              WM_event_add_mousemove(C);
 +
 +      return changed;
 +}
 +
 +/**
 + * Prepare context for manipulator handling (but only if area/region is
 + * part of screen). Version of #wm_handler_op_context for manipulators.
 + */
 +void wm_manipulatormap_handler_context(bContext *C, wmEventHandler *handler)
 +{
 +      bScreen *screen = CTX_wm_screen(C);
 +
 +      if (screen) {
 +              if (handler->op_area == NULL) {
 +                      /* do nothing in this context */
 +              }
 +              else {
 +                      ScrArea *sa;
 +
 +                      for (sa = screen->areabase.first; sa; sa = sa->next)
 +                              if (sa == handler->op_area)
 +                                      break;
 +                      if (sa == NULL) {
 +                              /* when changing screen layouts with running modal handlers (like render display), this
 +                               * is not an error to print */
 +                              if (handler->manipulator_map == NULL)
 +                                      printf("internal error: modal manipulator-map handler has invalid area\n");
 +                      }
 +                      else {
 +                              ARegion *ar;
 +                              CTX_wm_area_set(C, sa);
 +                              for (ar = sa->regionbase.first; ar; ar = ar->next)
 +                                      if (ar == handler->op_region)
 +                                              break;
 +                              /* XXX no warning print here, after full-area and back regions are remade */
 +                              if (ar)
 +                                      CTX_wm_region_set(C, ar);
 +                      }
 +              }
 +      }
 +}
 +
 +bool WM_manipulatormap_cursor_set(const wmManipulatorMap *mmap, wmWindow *win)
 +{
 +      wmManipulator *mpr = mmap->mmap_context.highlight;
 +      if (mpr && mpr->type->cursor_get) {
 +              WM_cursor_set(win, mpr->type->cursor_get(mpr));
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +void wm_manipulatormap_highlight_set(
 +        wmManipulatorMap *mmap, const bContext *C, wmManipulator *mpr, int part)
 +{
 +      if ((mpr != mmap->mmap_context.highlight) ||
 +          (mpr && part != mpr->highlight_part))
 +      {
 +              if (mmap->mmap_context.highlight) {
 +                      mmap->mmap_context.highlight->state &= ~WM_MANIPULATOR_STATE_HIGHLIGHT;
 +                      mmap->mmap_context.highlight->highlight_part = -1;
 +              }
 +
 +              mmap->mmap_context.highlight = mpr;
 +
 +              if (mpr) {
 +                      mpr->state |= WM_MANIPULATOR_STATE_HIGHLIGHT;
 +                      mpr->highlight_part = part;
 +
 +                      if (C && mpr->type->cursor_get) {
 +                              wmWindow *win = CTX_wm_window(C);
 +                              WM_cursor_set(win, mpr->type->cursor_get(mpr));
 +                      }
 +              }
 +              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_highlight_get(wmManipulatorMap *mmap)
 +{
 +      return mmap->mmap_context.highlight;
 +}
 +
 +/**
 + * Caller should call exit when (enable == False).
 + */
 +void wm_manipulatormap_modal_set(
 +        wmManipulatorMap *mmap, bContext *C, wmManipulator *mpr, const wmEvent *event, bool enable)
 +{
 +      if (enable) {
 +              BLI_assert(mmap->mmap_context.modal == NULL);
 +              wmWindow *win = CTX_wm_window(C);
 +
- void WM_manipulatormap_tooltip_create(
-         bContext *C, wmManipulatorMap *mmap)
- {
-       WM_manipulatormap_tooltip_clear(C, mmap);
-       if (mmap->mmap_context.highlight) {
-               mmap->mmap_context.tooltip = UI_tooltip_create_from_manipulator(C, mmap->mmap_context.highlight);
-       }
- }
- void WM_manipulatormap_tooltip_clear(
-         bContext *C, wmManipulatorMap *mmap)
- {
-       if (mmap->mmap_context.tooltip_timer != NULL) {
-               wmWindowManager *wm = CTX_wm_manager(C);
-               wmWindow *win = CTX_wm_window(C);
-               WM_event_remove_timer(wm, win, mmap->mmap_context.tooltip_timer);
-               mmap->mmap_context.tooltip_timer = NULL;
-       }
-       if (mmap->mmap_context.tooltip != NULL) {
-               UI_tooltip_free(C, mmap->mmap_context.tooltip);
-               mmap->mmap_context.tooltip = NULL;
-       }
- }
- void WM_manipulatormap_tooltip_timer_init(
-         bContext *C, wmManipulatorMap *mmap)
++              WM_tooltip_clear(C, win);
 +
 +              if (mpr->type->invoke &&
 +                  (mpr->type->modal || mpr->custom_modal))
 +              {
 +                      const int retval = mpr->type->invoke(C, mpr, event);
 +                      if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
 +                              return;
 +                      }
 +              }
 +
 +              mpr->state |= WM_MANIPULATOR_STATE_MODAL;
 +              mmap->mmap_context.modal = mpr;
 +
 +              if ((mpr->flag & WM_MANIPULATOR_GRAB_CURSOR) &&
 +                  (event->is_motion_absolute == false))
 +              {
 +                      WM_cursor_grab_enable(win, true, true, NULL);
 +                      copy_v2_v2_int(mmap->mmap_context.event_xy, &event->x);
 +                      mmap->mmap_context.event_grabcursor = win->grabcursor;
 +              }
 +              else {
 +                      mmap->mmap_context.event_xy[0] = INT_MAX;
 +              }
 +
 +              struct wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, mpr->highlight_part);
 +              if (mpop && mpop->type) {
 +                      const int retval = WM_operator_name_call_ptr(C, mpop->type, WM_OP_INVOKE_DEFAULT, &mpop->ptr);
 +                      if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
 +                              wm_manipulatormap_modal_set(mmap, C, mpr, event, false);
 +                      }
 +
 +                      /* we failed to hook the manipulator to the operator handler or operator was cancelled, return */
 +                      if (!mmap->mmap_context.modal) {
 +                              mpr->state &= ~WM_MANIPULATOR_STATE_MODAL;
 +                              MEM_SAFE_FREE(mpr->interaction_data);
 +                      }
 +                      return;
 +              }
 +      }
 +      else {
 +              BLI_assert(ELEM(mmap->mmap_context.modal, NULL, mpr));
 +
 +              /* deactivate, manipulator but first take care of some stuff */
 +              if (mpr) {
 +                      mpr->state &= ~WM_MANIPULATOR_STATE_MODAL;
 +                      MEM_SAFE_FREE(mpr->interaction_data);
 +              }
 +              mmap->mmap_context.modal = NULL;
 +
 +              if (C) {
 +                      wmWindow *win = CTX_wm_window(C);
 +                      if (mmap->mmap_context.event_xy[0] != INT_MAX) {
 +                              /* Check if some other part of Blender (typically operators)
 +                               * have adjusted the grab mode since it was set.
 +                               * If so: warp, so we have a predictable outcome. */
 +                              if (mmap->mmap_context.event_grabcursor == win->grabcursor) {
 +                                      WM_cursor_grab_disable(win, mmap->mmap_context.event_xy);
 +                              }
 +                              else {
 +                                      WM_cursor_warp(win, UNPACK2(mmap->mmap_context.event_xy));
 +                              }
 +                      }
 +                      ED_region_tag_redraw(CTX_wm_region(C));
 +                      WM_event_add_mousemove(C);
 +              }
 +
 +              mmap->mmap_context.event_xy[0] = INT_MAX;
 +      }
 +}
 +
 +wmManipulator *wm_manipulatormap_modal_get(wmManipulatorMap *mmap)
 +{
 +      return mmap->mmap_context.modal;
 +}
 +
 +wmManipulator **wm_manipulatormap_selected_get(wmManipulatorMap *mmap, int *r_selected_len)
 +{
 +      *r_selected_len = mmap->mmap_context.select.len;
 +      return mmap->mmap_context.select.items;
 +}
 +
 +ListBase *wm_manipulatormap_groups_get(wmManipulatorMap *mmap)
 +{
 +      return &mmap->groups;
 +}
 +
 +void WM_manipulatormap_message_subscribe(
 +        bContext *C, wmManipulatorMap *mmap, ARegion *ar, struct wmMsgBus *mbus)
 +{
 +      for (wmManipulatorGroup *mgroup = mmap->groups.first; mgroup; mgroup = mgroup->next) {
 +              if (!wm_manipulatorgroup_is_visible(mgroup, C)) {
 +                      continue;
 +              }
 +              for (wmManipulator *mpr = mgroup->manipulators.first; mpr; mpr = mpr->next) {
 +                      if (mpr->flag & WM_MANIPULATOR_HIDDEN) {
 +                              continue;
 +                      }
 +                      WM_manipulator_target_property_subscribe_all(mpr, mbus, ar);
 +              }
 +              if (mgroup->type->message_subscribe != NULL) {
 +                      mgroup->type->message_subscribe(C, mgroup, mbus);
 +              }
 +      }
 +}
 +
 +/** \} */ /* wmManipulatorMap */
 +
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Tooltip Handling
 + *
 + * \{ */
 +
-       if (mmap->mmap_context.tooltip_timer == NULL) {
-               wmWindowManager *wm = CTX_wm_manager(C);
-               wmWindow *win = CTX_wm_window(C);
-               /* TODO: BUTTON_TOOLTIP_DELAY */
-               mmap->mmap_context.tooltip_timer = WM_event_add_timer(wm, win, TIMER, UI_TOOLTIP_DELAY);
++struct ARegion *WM_manipulatormap_tooltip_init(
++        struct bContext *C, struct ARegion *ar, bool *r_exit_on_event)
 +{
- }
- const void *WM_manipulatormap_tooltip_timer_get(wmManipulatorMap *mmap)
- {
-       return mmap->mmap_context.tooltip_timer;
++      wmManipulatorMap *mmap = ar->manipulator_map;
++      *r_exit_on_event = true;
++      if (mmap) {
++              wmManipulator *mpr = mmap->mmap_context.highlight;
++              if (mpr) {
++                      return UI_tooltip_create_from_manipulator(C, mpr);
++              }
 +      }
++      return NULL;
 +}
 +
 +/** \} */ /* wmManipulatorMapType */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmManipulatorMapType
 + *
 + * \{ */
 +
 +wmManipulatorMapType *WM_manipulatormaptype_find(
 +        const struct wmManipulatorMapType_Params *mmap_params)
 +{
 +      for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) {
 +              if (mmap_type->spaceid == mmap_params->spaceid &&
 +                  mmap_type->regionid == mmap_params->regionid)
 +              {
 +                      return mmap_type;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +wmManipulatorMapType *WM_manipulatormaptype_ensure(
 +        const struct wmManipulatorMapType_Params *mmap_params)
 +{
 +      wmManipulatorMapType *mmap_type = WM_manipulatormaptype_find(mmap_params);
 +
 +      if (mmap_type) {
 +              return mmap_type;
 +      }
 +
 +      mmap_type = MEM_callocN(sizeof(wmManipulatorMapType), "manipulatortype list");
 +      mmap_type->spaceid = mmap_params->spaceid;
 +      mmap_type->regionid = mmap_params->regionid;
 +      BLI_addhead(&manipulatormaptypes, mmap_type);
 +
 +      return mmap_type;
 +}
 +
 +void wm_manipulatormaptypes_free(void)
 +{
 +      for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first, *mmap_type_next;
 +           mmap_type;
 +           mmap_type = mmap_type_next)
 +      {
 +              mmap_type_next = mmap_type->next;
 +              for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_next;
 +                   wgt_ref;
 +                   wgt_ref = wgt_next)
 +              {
 +                      wgt_next = wgt_ref->next;
 +                      WM_manipulatormaptype_group_free(wgt_ref);
 +              }
 +              MEM_freeN(mmap_type);
 +      }
 +}
 +
 +/**
 + * Initialize keymaps for all existing manipulator-groups
 + */
 +void wm_manipulators_keymap(wmKeyConfig *keyconf)
 +{
 +      /* we add this item-less keymap once and use it to group manipulator-group keymaps into it */
 +      WM_keymap_find(keyconf, "Manipulators", 0, 0);
 +
 +      for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first; mmap_type; mmap_type = mmap_type->next) {
 +              for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first; wgt_ref; wgt_ref = wgt_ref->next) {
 +                      wm_manipulatorgrouptype_setup_keymap(wgt_ref->type, keyconf);
 +              }
 +      }
 +}
 +
 +/** \} */ /* wmManipulatorMapType */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Updates for Dynamic Type Registraion
 + *
 + * \{ */
 +
 +
 +void WM_manipulatorconfig_update_tag_init(
 +        wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
 +{
 +      /* tag for update on next use */
 +      mmap_type->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT);
 +      wgt->type_update_flag |= (WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT);
 +
 +      wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT;
 +}
 +
 +void WM_manipulatorconfig_update_tag_remove(
 +        wmManipulatorMapType *mmap_type, wmManipulatorGroupType *wgt)
 +{
 +      /* tag for update on next use */
 +      mmap_type->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
 +      wgt->type_update_flag |= WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
 +
 +      wm_mmap_type_update_flag |= WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE;
 +}
 +
 +/**
 + * Run incase new types have been added (runs often, early exit where possible).
 + * Follows #WM_keyconfig_update concentions.
 + */
 +void WM_manipulatorconfig_update(struct Main *bmain)
 +{
 +      if (G.background)
 +              return;
 +
 +      if (wm_mmap_type_update_flag == 0)
 +              return;
 +
 +      if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) {
 +              for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first;
 +                   mmap_type;
 +                   mmap_type = mmap_type->next)
 +              {
 +                      if (mmap_type->type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE) {
 +                              mmap_type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_REMOVE;
 +                              for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first, *wgt_ref_next;
 +                                   wgt_ref;
 +                                   wgt_ref = wgt_ref_next)
 +                              {
 +                                      wgt_ref_next = wgt_ref->next;
 +                                      if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_REMOVE) {
 +                                              WM_manipulatormaptype_group_unlink(NULL, bmain, mmap_type, wgt_ref->type);
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_REMOVE;
 +      }
 +
 +      if (wm_mmap_type_update_flag & WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT) {
 +              for (wmManipulatorMapType *mmap_type = manipulatormaptypes.first;
 +                   mmap_type;
 +                   mmap_type = mmap_type->next)
 +              {
 +                      const uchar type_update_all = WM_MANIPULATORMAPTYPE_UPDATE_INIT | WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
 +                      if (mmap_type->type_update_flag & type_update_all) {
 +                              mmap_type->type_update_flag &= ~type_update_all;
 +                              for (wmManipulatorGroupTypeRef *wgt_ref = mmap_type->grouptype_refs.first;
 +                                   wgt_ref;
 +                                   wgt_ref = wgt_ref->next)
 +                              {
 +                                      if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_KEYMAP_INIT) {
 +                                              WM_manipulatormaptype_group_init_runtime_keymap(bmain, wgt_ref->type);
 +                                              wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_KEYMAP_INIT;
 +                                      }
 +
 +                                      if (wgt_ref->type->type_update_flag & WM_MANIPULATORMAPTYPE_UPDATE_INIT) {
 +                                              WM_manipulatormaptype_group_init_runtime(bmain, mmap_type, wgt_ref->type);
 +                                              wgt_ref->type->type_update_flag &= ~WM_MANIPULATORMAPTYPE_UPDATE_INIT;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              wm_mmap_type_update_flag &= ~WM_MANIPULATORMAPTYPE_GLOBAL_UPDATE_INIT;
 +      }
 +}
 +
 +/** \} */