Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Fri, 31 Aug 2018 03:40:10 +0000 (13:40 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 31 Aug 2018 03:40:10 +0000 (13:40 +1000)
48 files changed:
1  2 
source/blender/editors/animation/anim_channels_edit.c
source/blender/editors/animation/anim_markers.c
source/blender/editors/animation/anim_ops.c
source/blender/editors/armature/armature_ops.c
source/blender/editors/curve/curve_ops.c
source/blender/editors/gpencil/gpencil_ops.c
source/blender/editors/interface/interface_ops.c
source/blender/editors/interface/view2d_ops.c
source/blender/editors/lattice/lattice_ops.c
source/blender/editors/mask/mask_edit.c
source/blender/editors/mesh/mesh_ops.c
source/blender/editors/metaball/mball_ops.c
source/blender/editors/object/object_ops.c
source/blender/editors/physics/physics_ops.c
source/blender/editors/screen/area.c
source/blender/editors/screen/screen_ops.c
source/blender/editors/sculpt_paint/paint_ops.c
source/blender/editors/space_action/action_ops.c
source/blender/editors/space_action/space_action.c
source/blender/editors/space_buttons/space_buttons.c
source/blender/editors/space_clip/space_clip.c
source/blender/editors/space_console/space_console.c
source/blender/editors/space_file/space_file.c
source/blender/editors/space_graph/graph_ops.c
source/blender/editors/space_graph/space_graph.c
source/blender/editors/space_image/space_image.c
source/blender/editors/space_info/space_info.c
source/blender/editors/space_nla/nla_ops.c
source/blender/editors/space_nla/space_nla.c
source/blender/editors/space_node/node_ops.c
source/blender/editors/space_node/space_node.c
source/blender/editors/space_outliner/outliner_ops.c
source/blender/editors/space_outliner/space_outliner.c
source/blender/editors/space_script/space_script.c
source/blender/editors/space_sequencer/sequencer_ops.c
source/blender/editors/space_sequencer/space_sequencer.c
source/blender/editors/space_text/space_text.c
source/blender/editors/space_topbar/space_topbar.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/editors/space_view3d/view3d_ops.c
source/blender/editors/uvedit/uvedit_ops.c
source/blender/makesrna/intern/rna_wm_api.c
source/blender/windowmanager/WM_keymap.h
source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
source/blender/windowmanager/intern/wm_keymap.c
source/blender/windowmanager/intern/wm_operators.c
source/blender/windowmanager/intern/wm_window.c

@@@ -193,9 -201,26 +193,9 @@@ void ED_keymap_armature(wmKeyConfig *ke
        wmKeyMapItem *kmi;
  
        /* Armature ------------------------ */
-       keymap = WM_keymap_find(keyconf, "Armature", 0, 0);
+       keymap = WM_keymap_ensure(keyconf, "Armature", 0, 0);
        keymap->poll = ED_operator_editarmature;
  
 -      /* Armature -> Etch-A-Ton ------------------------ */
 -      WM_keymap_add_item(keymap, "SKETCH_OT_delete", XKEY, KM_PRESS, 0, 0);
 -      WM_keymap_add_item(keymap, "SKETCH_OT_delete", DELKEY, KM_PRESS, 0, 0);
 -      WM_keymap_add_item(keymap, "SKETCH_OT_finish_stroke", RIGHTMOUSE, KM_PRESS, 0, 0);
 -      WM_keymap_add_item(keymap, "SKETCH_OT_cancel_stroke", ESCKEY, KM_PRESS, 0, 0);
 -      /* Already part of view3d select */
 -      //WM_keymap_add_item(keymap, "SKETCH_OT_select", SELECTMOUSE, KM_PRESS, 0, 0);
 -
 -      /* sketch poll checks mode */
 -      WM_keymap_add_item(keymap, "SKETCH_OT_gesture", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
 -      WM_keymap_add_item(keymap, "SKETCH_OT_draw_stroke", LEFTMOUSE, KM_PRESS, 0, 0);
 -      kmi = WM_keymap_add_item(keymap, "SKETCH_OT_draw_stroke", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
 -      RNA_boolean_set(kmi->ptr, "snap", true);
 -      WM_keymap_add_item(keymap, "SKETCH_OT_draw_preview", MOUSEMOVE, KM_ANY, 0, 0);
 -      kmi = WM_keymap_add_item(keymap, "SKETCH_OT_draw_preview", MOUSEMOVE, KM_ANY, KM_CTRL, 0);
 -      RNA_boolean_set(kmi->ptr, "snap", true);
 -
        /* only set in editmode armature, by space_view3d listener */
        kmi = WM_keymap_add_item(keymap, "ARMATURE_OT_hide", HKEY, KM_PRESS, 0, 0);
        RNA_boolean_set(kmi->ptr, "unselected", false);
@@@ -230,10 -229,10 +230,10 @@@ void ED_keymap_curve(wmKeyConfig *keyco
        RNA_boolean_set(kmi->ptr, "accent", true); /* accented characters */
  
        /* only set in editmode curve, by space_view3d listener */
-       keymap = WM_keymap_find(keyconf, "Curve", 0, 0);
+       keymap = WM_keymap_ensure(keyconf, "Curve", 0, 0);
        keymap->poll = ED_operator_editsurfcurve;
  
 -      WM_keymap_add_menu(keymap, "INFO_MT_edit_curve_add", AKEY, KM_PRESS, KM_SHIFT, 0);
 +      WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_curve_add", AKEY, KM_PRESS, KM_SHIFT, 0);
  
        WM_keymap_add_item(keymap, "CURVE_OT_handle_type_set", VKEY, KM_PRESS, 0, 0);
  
  /* ****************************************** */
  /* Grease Pencil Keymaps */
  
 -/* Generic Drawing Keymap */
 +/* Generic Drawing Keymap - Annotations */
  static void ed_keymap_gpencil_general(wmKeyConfig *keyconf)
  {
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil", 0, 0);
+       wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil", 0, 0);
        wmKeyMapItem *kmi;
  
        /* Draw  --------------------------------------- */
@@@ -252,112 -231,6 +252,112 @@@ static void ed_keymap_gpencil_selection
        /* select more/less */
        WM_keymap_add_item(keymap, "GPENCIL_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
        WM_keymap_add_item(keymap, "GPENCIL_OT_select_less", PADMINUS, KM_PRESS, KM_CTRL, 0);
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Edit Mode", 0, 0);
 +}
 +
 +static void ed_keymap_gpencil_display(wmKeyMap *keymap)
 +{
 +      wmKeyMapItem *kmi;
 +
 +      kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", QKEY, KM_PRESS, KM_SHIFT, 0);
 +      RNA_string_set(kmi->ptr, "data_path", "space_data.overlay.use_gpencil_edit_lines");
 +
 +      kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", QKEY, KM_PRESS, KM_SHIFT | KM_ALT, 0);
 +      RNA_string_set(kmi->ptr, "data_path", "space_data.overlay.use_gpencil_multiedit_line_only");
 +}
 +
 +static void ed_keymap_gpencil_sculpt(wmKeyMap *keymap)
 +{
 +      wmKeyMapItem *kmi;
 +
 +      /* Pie Menu - For settings/tools easy access */
 +      WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_sculpt", EKEY, KM_PRESS, 0, DKEY);
 +
 +      /* Sculpting ------------------------------------- */
 +
 +      /* Brush-Based Editing:
 +      *   EKEY + LMB                          = Single stroke, draw immediately
 +      *        + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc.
 +      *
 +      * For the modal version, use D+E -> Sculpt
 +      */
 +      /* GPXX: disabled to make toolsystem works */
 +      //kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, 0);
 +      //RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +      RNA_boolean_set(kmi->ptr, "keep_brush", true);
 +      /*RNA_boolean_set(kmi->ptr, "use_invert", true);*/
 +
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +      RNA_boolean_set(kmi->ptr, "keep_brush", true);
 +      /*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/
 +
 +      /* Shift-FKEY = Sculpt Strength */
 +      kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0);
 +      RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength");
 +
 +      /* FKEY = Sculpt Brush Size */
 +      kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0);
 +      RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size");
 +
 +      /* menu sculpt specials */
 +      WM_keymap_add_menu(keymap, "VIEW3D_MT_gpencil_sculpt_specials", WKEY, KM_PRESS, 0, 0);
 +}
 +
 +static void ed_keymap_gpencil_weight(wmKeyMap *keymap)
 +{
 +      wmKeyMapItem *kmi;
 +
 +
 +      /* Brush-Based Editing:
 +      *   EKEY + LMB                          = Single stroke, draw immediately
 +      *        + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc.
 +      *
 +      * For the modal version, use D+E -> Sculpt
 +      */
 +      /* GPXX: disabled to make toolsystem works */
 +      //kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, 0);
 +      //RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +      RNA_boolean_set(kmi->ptr, "keep_brush", true);
 +      /*RNA_boolean_set(kmi->ptr, "use_invert", true);*/
 +
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +      RNA_boolean_set(kmi->ptr, "keep_brush", true);
 +      /*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/
 +}
 +
 +/* Stroke Editing Keymap - Only when editmode is enabled */
 +static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
 +{
++      wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Edit Mode", 0, 0);
 +      wmKeyMapItem *kmi;
 +
 +      /* set poll callback - so that this keymap only gets enabled when stroke editmode is enabled */
 +      keymap->poll = gp_stroke_editmode_poll;
 +
 +      /* ----------------------------------------------- */
 +
 +      /* Brush Settings */
 +      /* NOTE: We cannot expose these in the standard keymap, as they will interfere with regular hotkeys
 +       *       in other modes. However, when we are dealing with Stroke Edit Mode, we know for certain
 +       *       that the only data being edited is that of the Grease Pencil strokes
 +       */
 +
 +      /* Interpolation */
 +      WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate", EKEY, KM_PRESS, KM_CTRL | KM_ALT, 0);
 +      WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate_sequence", EKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0);
 +
 +      /* normal select */
 +      WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, 0, 0);
 +
 +      /* Selection */
 +      ed_keymap_gpencil_selection(keymap);
  
        /* Editing ----------------------------------------- */
  
        /* Proportional Editing */
        ED_keymap_proportional_cycle(keyconf, keymap);
        ED_keymap_proportional_editmode(keyconf, keymap, true);
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0);
 +
 +      /* menu - add GP object (3d view only) */
 +      WM_keymap_add_item(keymap, "OBJECT_OT_gpencil_add", AKEY, KM_PRESS, KM_SHIFT, 0);
 +
 +      /* menu vertex group */
 +      WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_vertex_group", GKEY, KM_PRESS, KM_CTRL, 0);
 +
 +      /* toggle edit mode */
 +      WM_keymap_add_item(keymap, "GPENCIL_OT_editmode_toggle", TABKEY, KM_PRESS, 0, 0);
 +}
 +
 +/* keys for draw with a drawing brush (no fill) */
 +static void ed_keymap_gpencil_painting_draw(wmKeyConfig *keyconf)
 +{
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint (Erase)", 0, 0);
++      wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0);
 +      wmKeyMapItem *kmi;
 +
 +      /* set poll callback */
 +      keymap->poll = gp_stroke_paintmode_draw_poll;
 +
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, 0, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +
 +      /* draw - straight lines */
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_ALT, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_STRAIGHT);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +
 +      /* draw - poly lines */
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_POLY);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +
 +      /* erase */
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +
 +      /* Tablet Mappings for Drawing ------------------ */
 +      /* For now, only support direct drawing using the eraser, as most users using a tablet
 +      * may still want to use that as their primary pointing device!
 +      */
 +#if 0
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_STYLUS, KM_PRESS, 0, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +#endif
 +
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_ERASER, KM_PRESS, 0, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +
 +      /* Selection (used by eraser) */
 +      /* border select */
 +      WM_keymap_add_item(keymap, "GPENCIL_OT_select_border", BKEY, KM_PRESS, 0, 0);
 +
 +      /* lasso select */
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0);
 +      RNA_boolean_set(kmi->ptr, "deselect", false);
 +}
 +
 +/* keys for draw with a eraser brush (erase) */
 +static void ed_keymap_gpencil_painting_erase(wmKeyConfig *keyconf)
 +{
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint (Fill)", 0, 0);
++      wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Erase)", 0, 0);
 +      wmKeyMapItem *kmi;
 +
 +      /* set poll callback */
 +      keymap->poll = gp_stroke_paintmode_erase_poll;
 +
 +      /* erase */
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, 0, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_ERASER, KM_PRESS, 0, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +
 +      /* Selection (used by eraser) */
 +      /* border select */
 +      WM_keymap_add_item(keymap, "GPENCIL_OT_select_border", BKEY, KM_PRESS, 0, 0);
 +
 +      /* lasso select */
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0);
 +      RNA_boolean_set(kmi->ptr, "deselect", false);
 +}
 +
 +/* keys for draw with a fill brush */
 +static void ed_keymap_gpencil_painting_fill(wmKeyConfig *keyconf)
 +{
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint Mode", 0, 0);
++      wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Fill)", 0, 0);
 +      wmKeyMapItem *kmi;
 +
 +      /* set poll callback */
 +      keymap->poll = gp_stroke_paintmode_fill_poll;
 +
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_fill", LEFTMOUSE, KM_PRESS, 0, 0);
 +      RNA_boolean_set(kmi->ptr, "on_back", false);
 +
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_fill", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
 +      RNA_boolean_set(kmi->ptr, "on_back", true);
 +
 +      /* if press alternative key, the brush now it's for drawing areas */
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +      /* disable straight lines */
 +      RNA_boolean_set(kmi->ptr, "disable_straight", true);
 +
 +      /* if press alternative key, the brush now it's for drawing lines */
 +      kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_ALT, 0);
 +      RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW);
 +      RNA_boolean_set(kmi->ptr, "wait_for_input", false);
 +      /* disable straight lines */
 +      RNA_boolean_set(kmi->ptr, "disable_straight", true);
 +      /* enable special stroke with no fill flag */
 +      RNA_boolean_set(kmi->ptr, "disable_fill", true);
 +}
 +
 +/* Stroke Painting Keymap - Only when paintmode is enabled */
 +static void ed_keymap_gpencil_painting(wmKeyConfig *keyconf)
 +{
++      wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint Mode", 0, 0);
 +      wmKeyMapItem *kmi;
 +
 +      /* set poll callback - so that this keymap only gets enabled when stroke paintmode is enabled */
 +      keymap->poll = gp_stroke_paintmode_poll;
 +
 +      /* FKEY = Brush Size */
 +      kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0);
 +      RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_paint.brush.size");
 +
 +      /* CTRL + FKEY = Eraser Radius */
 +      kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0);
 +      RNA_string_set(kmi->ptr, "data_path_primary", "user_preferences.edit.grease_pencil_eraser_radius");
 +
 +      /* menu draw specials */
 +      WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_draw_specials", WKEY, KM_PRESS, 0, 0);
 +
 +      /* menu draw delete */
 +      WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_draw_delete", XKEY, KM_PRESS, 0, 0);
 +
  }
  
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Sculpt Mode", 0, 0);
 +/* Stroke Sculpting Keymap - Only when sculptmode is enabled */
 +static void ed_keymap_gpencil_sculpting(wmKeyConfig *keyconf)
 +{
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Weight Mode", 0, 0);
++      wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Sculpt Mode", 0, 0);
 +
 +      /* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */
 +      keymap->poll = gp_stroke_sculptmode_poll;
 +
 +      /* Selection */
 +      ed_keymap_gpencil_selection(keymap);
 +
 +      /* sculpt */
 +      ed_keymap_gpencil_sculpt(keymap);
 +
 +      /* Display. */
 +      ed_keymap_gpencil_display(keymap);
 +}
 +
 +/* Stroke Weight Paint Keymap - Only when weight is enabled */
 +static void ed_keymap_gpencil_weightpainting(wmKeyConfig *keyconf)
 +{
++      wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Weight Mode", 0, 0);
 +      wmKeyMapItem *kmi;
 +
 +      /* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */
 +      keymap->poll = gp_stroke_weightmode_poll;
 +
 +      /* Selection */
 +      ed_keymap_gpencil_selection(keymap);
 +
 +      /* sculpt */
 +      ed_keymap_gpencil_weight(keymap);
 +
 +      /* Shift-FKEY = Sculpt Strength */
 +      kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0);
 +      RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.weight_brush.strength");
 +
 +      /* FKEY = Sculpt Brush Size */
 +      kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0);
 +      RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.weight_brush.size");
 +
 +      /* Display. */
 +      ed_keymap_gpencil_display(keymap);
 +}
  /* ==================== */
  
  void ED_keymap_gpencil(wmKeyConfig *keyconf)
@@@ -327,25 -295,15 +327,25 @@@ void ED_keymap_mesh(wmKeyConfig *keycon
  {
        wmKeyMap *keymap;
        wmKeyMapItem *kmi;
 -      int i;
  
-       keymap = WM_keymap_find(keyconf, "Mesh", 0, 0);
+       keymap = WM_keymap_ensure(keyconf, "Mesh", 0, 0);
        keymap->poll = ED_operator_editmesh;
  
 -      WM_keymap_add_item(keymap, "MESH_OT_loopcut_slide", RKEY, KM_PRESS, KM_CTRL, 0);
 -      WM_keymap_add_item(keymap, "MESH_OT_offset_edge_loops_slide", RKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
 +      kmi = WM_keymap_add_item(keymap, "MESH_OT_loopcut_slide", RKEY, KM_PRESS, KM_CTRL, 0);
 +      {
 +              PointerRNA macro_ptr = RNA_pointer_get(kmi->ptr, "TRANSFORM_OT_edge_slide");
 +              RNA_boolean_set(&macro_ptr, "release_confirm", false);
 +      }
 +      kmi = WM_keymap_add_item(keymap, "MESH_OT_offset_edge_loops_slide", RKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
 +      {
 +              PointerRNA macro_ptr = RNA_pointer_get(kmi->ptr, "TRANSFORM_OT_edge_slide");
 +              RNA_boolean_set(&macro_ptr, "release_confirm", false);
 +      }
 +
        WM_keymap_add_item(keymap, "MESH_OT_inset", IKEY, KM_PRESS, 0, 0);
 +#ifdef USE_WM_KEYMAP_27X
        WM_keymap_add_item(keymap, "MESH_OT_poke", PKEY, KM_PRESS, KM_ALT, 0);
 +#endif
        kmi = WM_keymap_add_item(keymap, "MESH_OT_bevel", BKEY, KM_PRESS, KM_CTRL, 0);
        RNA_boolean_set(kmi->ptr, "vertex_only", false);
        kmi = WM_keymap_add_item(keymap, "MESH_OT_bevel", BKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
@@@ -306,11 -284,12 +306,11 @@@ void ED_keymap_object(wmKeyConfig *keyc
  {
        wmKeyMap *keymap;
        wmKeyMapItem *kmi;
 -      int i;
  
        /* Objects, Regardless of Mode -------------------------------------------------- */
-       keymap = WM_keymap_find(keyconf, "Object Non-modal", 0, 0);
+       keymap = WM_keymap_ensure(keyconf, "Object Non-modal", 0, 0);
  
 -      /* Note: this keymap works disregarding mode */
 +      /* modes */
        kmi = WM_keymap_add_item(keymap, "OBJECT_OT_mode_set", TABKEY, KM_PRESS, 0, 0);
        RNA_enum_set(kmi->ptr, "mode", OB_MODE_EDIT);
        RNA_boolean_set(kmi->ptr, "toggle", true);
@@@ -1454,17 -1388,21 +1454,17 @@@ static void ed_default_handlers(wmWindo
        }
        if (flag & ED_KEYMAP_MARKERS) {
                /* time-markers */
-               wmKeyMap *keymap = WM_keymap_find(wm->defaultconf, "Markers", 0, 0);
+               wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "Markers", 0, 0);
  
 -              /* time space only has this keymap, the others get a boundbox restricted map */
 -              if (sa->spacetype != SPACE_TIME) {
 -                      ARegion *ar;
 -                      /* same local check for all areas */
 -                      static rcti rect = {0, 10000, 0, -1};
 -                      rect.ymax = UI_MARKER_MARGIN_Y;
 -                      ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
 -                      if (ar) {
 -                              WM_event_add_keymap_handler_bb(handlers, keymap, &rect, &ar->winrct);
 -                      }
 +              /* use a boundbox restricted map */
 +              ARegion *ar;
 +              /* same local check for all areas */
 +              static rcti rect = {0, 10000, 0, -1};
 +              rect.ymax = UI_MARKER_MARGIN_Y;
 +              ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
 +              if (ar) {
 +                      WM_event_add_keymap_handler_bb(handlers, keymap, &rect, &ar->winrct);
                }
 -              else
 -                      WM_event_add_keymap_handler(handlers, keymap);
        }
        if (flag & ED_KEYMAP_ANIMATION) {
                /* frame changing and timeline operators (for time spaces) */
        }
        if (flag & ED_KEYMAP_GPENCIL) {
                /* grease pencil */
 -              /* NOTE: This is now 2 keymaps - One for basic functionality,
 -               *       and one that only applies when "Edit Mode" is enabled
 -               *       for strokes.
 +              /* NOTE: This is now 4 keymaps - One for basic functionality,
 +               *       and others for special stroke modes (edit, paint and sculpt).
                 *
 -               *       For now, it's easier to just include both,
 -               *       since you hardly want one without the other.
 +               *       For now, it's easier to just include all,
 +               *       since you hardly want one without the others.
                 */
-               wmKeyMap *keymap_general = WM_keymap_find(wm->defaultconf, "Grease Pencil", 0, 0);
+               wmKeyMap *keymap_general = WM_keymap_ensure(wm->defaultconf, "Grease Pencil", 0, 0);
 -              wmKeyMap *keymap_edit = WM_keymap_ensure(wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
 -
                WM_event_add_keymap_handler(handlers, keymap_general);
-               wmKeyMap *keymap_edit = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
 +
++              wmKeyMap *keymap_edit = WM_keymap_ensure(wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
                WM_event_add_keymap_handler(handlers, keymap_edit);
-               wmKeyMap *keymap_paint = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint Mode", 0, 0);
 +
-               wmKeyMap *keymap_paint_draw = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0);
++              wmKeyMap *keymap_paint = WM_keymap_ensure(wm->defaultconf, "Grease Pencil Stroke Paint Mode", 0, 0);
 +              WM_event_add_keymap_handler(handlers, keymap_paint);
 +
-               wmKeyMap *keymap_paint_erase = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint (Erase)", 0, 0);
++              wmKeyMap *keymap_paint_draw = WM_keymap_ensure(wm->defaultconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0);
 +              WM_event_add_keymap_handler(handlers, keymap_paint_draw);
 +
-               wmKeyMap *keymap_paint_fill = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint (Fill)", 0, 0);
++              wmKeyMap *keymap_paint_erase = WM_keymap_ensure(wm->defaultconf, "Grease Pencil Stroke Paint (Erase)", 0, 0);
 +              WM_event_add_keymap_handler(handlers, keymap_paint_erase);
 +
-               wmKeyMap *keymap_sculpt = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Sculpt Mode", 0, 0);
++              wmKeyMap *keymap_paint_fill = WM_keymap_ensure(wm->defaultconf, "Grease Pencil Stroke Paint (Fill)", 0, 0);
 +              WM_event_add_keymap_handler(handlers, keymap_paint_fill);
 +
++              wmKeyMap *keymap_sculpt = WM_keymap_ensure(wm->defaultconf, "Grease Pencil Stroke Sculpt Mode", 0, 0);
 +              WM_event_add_keymap_handler(handlers, keymap_sculpt);
        }
        if (flag & ED_KEYMAP_HEADER) {
                /* standard keymap for headers regions */
@@@ -4886,12 -4627,12 +4886,12 @@@ void ED_keymap_screen(wmKeyConfig *keyc
  
        /* Header Editing ------------------------------------------------ */
        /* note: this is only used when the cursor is inside the header */
-       keymap = WM_keymap_find(keyconf, "Header", 0, 0);
+       keymap = WM_keymap_ensure(keyconf, "Header", 0, 0);
  
 -      WM_keymap_add_item(keymap, "SCREEN_OT_header_toolbox", RIGHTMOUSE, KM_PRESS, 0, 0);
 +      WM_keymap_add_item(keymap, "SCREEN_OT_header_context_menu", RIGHTMOUSE, KM_PRESS, 0, 0);
  
        /* Screen General ------------------------------------------------ */
-       keymap = WM_keymap_find(keyconf, "Screen", 0, 0);
+       keymap = WM_keymap_ensure(keyconf, "Screen", 0, 0);
  
        /* standard timers */
        WM_keymap_add_item(keymap, "SCREEN_OT_animation_step", TIMER0, KM_ANY, KM_ANY, 0);
  
  
        /* Anim Playback ------------------------------------------------ */
-       keymap = WM_keymap_find(keyconf, "Frames", 0, 0);
+       keymap = WM_keymap_ensure(keyconf, "Frames", 0, 0);
  
        /* frame offsets */
 +#ifdef USE_WM_KEYMAP_27X
        RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", UPARROWKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "delta", 10);
        RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", DOWNARROWKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "delta", -10);
 +#endif
        RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", LEFTARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", -1);
        RNA_int_set(WM_keymap_add_item(keymap, "SCREEN_OT_frame_offset", RIGHTARROWKEY, KM_PRESS, 0, 0)->ptr, "delta", 1);
  
@@@ -1435,12 -1396,10 +1435,12 @@@ void ED_keymap_paint(wmKeyConfig *keyco
        RNA_string_set(kmi->ptr, "data_path", "tool_settings.weight_paint.brush.use_smooth_stroke");
  
        /*Weight paint's Vertex Selection Mode */
-       keymap = WM_keymap_find(keyconf, "Weight Paint Vertex Selection", 0, 0);
+       keymap = WM_keymap_ensure(keyconf, "Weight Paint Vertex Selection", 0, 0);
        keymap->poll = vert_paint_poll;
        kmi = WM_keymap_add_item(keymap, "PAINT_OT_vert_select_all", AKEY, KM_PRESS, 0, 0);
 -      RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
 +      RNA_enum_set(kmi->ptr, "action", SEL_SELECT);
 +      kmi = WM_keymap_add_item(keymap, "PAINT_OT_vert_select_all", AKEY, KM_PRESS, KM_ALT, 0);
 +      RNA_enum_set(kmi->ptr, "action", SEL_DESELECT);
        kmi = WM_keymap_add_item(keymap, "PAINT_OT_vert_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
        RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
        WM_keymap_add_item(keymap, "VIEW3D_OT_select_border", BKEY, KM_PRESS, 0, 0);
@@@ -296,12 -266,10 +296,12 @@@ static void action_channel_region_init(
        UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy);
  
        /* own keymap */
-       keymap = WM_keymap_find(wm->defaultconf, "Animation Channels", 0, 0);
+       keymap = WM_keymap_ensure(wm->defaultconf, "Animation Channels", 0, 0);
        WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
  
-       keymap = WM_keymap_find(wm->defaultconf, "Dopesheet Generic", SPACE_ACTION, 0);
 +      WM_keymap_add_menu(keymap, "DOPESHEET_MT_specials_channels", WKEY, KM_PRESS, 0, 0);
 +
+       keymap = WM_keymap_ensure(wm->defaultconf, "Dopesheet Generic", SPACE_ACTION, 0);
        WM_event_add_keymap_handler(&ar->handlers, keymap);
  }
  
@@@ -330,9 -185,9 +330,9 @@@ static void buttons_operatortypes(void
  
  static void buttons_keymap(struct wmKeyConfig *keyconf)
  {
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Property Editor", SPACE_BUTS, 0);
+       wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Property Editor", SPACE_BUTS, 0);
  
 -      WM_keymap_add_item(keymap, "BUTTONS_OT_toolbox", RIGHTMOUSE, KM_PRESS, 0, 0);
 +      WM_keymap_add_item(keymap, "BUTTONS_OT_context_menu", RIGHTMOUSE, KM_PRESS, 0, 0);
  }
  
  /* add handlers, stuff you only do once or on area/region changes */
@@@ -667,18 -659,8 +667,18 @@@ static void image_main_region_init(wmWi
        // image space manages own v2d
        // UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_STANDARD, ar->winx, ar->winy);
  
 +      /* gizmos */
 +      if (ar->gizmo_map == NULL) {
 +              const struct wmGizmoMapType_Params wmap_params = {
 +                      .spaceid = SPACE_IMAGE,
 +                      .regionid = RGN_TYPE_WINDOW,
 +              };
 +              ar->gizmo_map = WM_gizmomap_new_from_type(&wmap_params);
 +      }
 +      WM_gizmomap_add_handlers(ar, ar->gizmo_map);
 +
        /* mask polls mode */
-       keymap = WM_keymap_find(wm->defaultconf, "Mask Editing", 0, 0);
+       keymap = WM_keymap_ensure(wm->defaultconf, "Mask Editing", 0, 0);
        WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
  
        /* image paint polls for mode */
        WM_event_add_keymap_handler(&ar->handlers, keymap);
  
        /* own keymaps */
-       keymap = WM_keymap_find(wm->defaultconf, "Image Generic", SPACE_IMAGE, 0);
+       keymap = WM_keymap_ensure(wm->defaultconf, "Image Generic", SPACE_IMAGE, 0);
        WM_event_add_keymap_handler(&ar->handlers, keymap);
-       keymap = WM_keymap_find(wm->defaultconf, "Image", SPACE_IMAGE, 0);
+       keymap = WM_keymap_ensure(wm->defaultconf, "Image", SPACE_IMAGE, 0);
        WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
 -
  }
  
  static void image_main_region_draw(const bContext *C, ARegion *ar)
@@@ -646,19 -643,11 +646,19 @@@ static void node_main_region_init(wmWin
  
        UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy);
  
 +      /* gizmos stay in the background for now - quick patchjob to make sure nodes themselves work */
 +      if (ar->gizmo_map == NULL) {
 +              ar->gizmo_map = WM_gizmomap_new_from_type(
 +                      &(const struct wmGizmoMapType_Params){SPACE_NODE, RGN_TYPE_WINDOW});
 +      }
 +
 +      WM_gizmomap_add_handlers(ar, ar->gizmo_map);
 +
        /* own keymaps */
-       keymap = WM_keymap_find(wm->defaultconf, "Node Generic", SPACE_NODE, 0);
+       keymap = WM_keymap_ensure(wm->defaultconf, "Node Generic", SPACE_NODE, 0);
        WM_event_add_keymap_handler(&ar->handlers, keymap);
  
-       keymap = WM_keymap_find(wm->defaultconf, "Node Editor", SPACE_NODE, 0);
+       keymap = WM_keymap_ensure(wm->defaultconf, "Node Editor", SPACE_NODE, 0);
        WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
  
        /* add drop boxes */
@@@ -120,11 -91,9 +120,11 @@@ void outliner_operatortypes(void
  
  void outliner_keymap(wmKeyConfig *keyconf)
  {
-       wmKeyMap *keymap = WM_keymap_find(keyconf, "Outliner", SPACE_OUTLINER, 0);
+       wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Outliner", SPACE_OUTLINER, 0);
        wmKeyMapItem *kmi;
  
 +      WM_keymap_add_item(keymap, "OUTLINER_OT_highlight_update", MOUSEMOVE, KM_ANY, KM_ANY, 0);
 +
        WM_keymap_add_item(keymap, "OUTLINER_OT_item_rename", LEFTMOUSE, KM_DBL_CLICK, 0, 0);
  
        kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, 0, 0);
@@@ -149,12 -148,10 +149,12 @@@ void sequencer_keymap(wmKeyConfig *keyc
        WM_keymap_add_item(keymap, "SEQUENCER_OT_view_toggle", TABKEY, KM_PRESS, KM_CTRL, 0);
  
        /* Strips Region --------------------------------------------------------------- */
-       keymap = WM_keymap_find(keyconf, "Sequencer", SPACE_SEQ, 0);
+       keymap = WM_keymap_ensure(keyconf, "Sequencer", SPACE_SEQ, 0);
  
        kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_select_all", AKEY, KM_PRESS, 0, 0);
 -      RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
 +      RNA_enum_set(kmi->ptr, "action", SEL_SELECT);
 +      kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_select_all", AKEY, KM_PRESS, KM_ALT, 0);
 +      RNA_enum_set(kmi->ptr, "action", SEL_DESELECT);
        kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
        RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
  
index 6113922,0000000..db75545
mode 100644,000000..100644
--- /dev/null
@@@ -1,286 -1,0 +1,286 @@@
-       keymap = WM_keymap_find(wm->defaultconf, "View2D Buttons List", 0, 0);
 +/*
 + * ***** 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) 2017 Blender Foundation.
 + * All rights reserved.
 + *
 + * Contributor(s): Blender Foundation
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/editors/space_topbar/space_topbar.c
 + *  \ingroup sptopbar
 + */
 +
 +
 +#include <string.h>
 +#include <stdio.h>
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_blenlib.h"
 +#include "BLI_utildefines.h"
 +
 +#include "BLO_readfile.h"
 +#include "BLT_translation.h"
 +
 +#include "BKE_context.h"
 +#include "BKE_global.h"
 +#include "BKE_screen.h"
 +
 +#include "ED_screen.h"
 +#include "ED_space_api.h"
 +#include "ED_undo.h"
 +
 +#include "UI_interface.h"
 +#include "UI_resources.h"
 +#include "UI_view2d.h"
 +
 +#include "RNA_access.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +#include "WM_message.h"
 +
 +/* ******************** default callbacks for topbar space ***************** */
 +
 +static SpaceLink *topbar_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
 +{
 +      ARegion *ar;
 +      SpaceTopBar *stopbar;
 +
 +      stopbar = MEM_callocN(sizeof(*stopbar), "init topbar");
 +      stopbar->spacetype = SPACE_TOPBAR;
 +
 +      /* header */
 +      ar = MEM_callocN(sizeof(ARegion), "left aligned header for topbar");
 +      BLI_addtail(&stopbar->regionbase, ar);
 +      ar->regiontype = RGN_TYPE_HEADER;
 +      ar->alignment = RGN_ALIGN_TOP;
 +      ar = MEM_callocN(sizeof(ARegion), "right aligned header for topbar");
 +      BLI_addtail(&stopbar->regionbase, ar);
 +      ar->regiontype = RGN_TYPE_HEADER;
 +      ar->alignment = RGN_ALIGN_RIGHT | RGN_SPLIT_PREV;
 +
 +      /* main regions */
 +      ar = MEM_callocN(sizeof(ARegion), "left aligned main region for topbar");
 +      BLI_addtail(&stopbar->regionbase, ar);
 +      ar->regiontype = RGN_TYPE_WINDOW;
 +      ar->alignment = RGN_ALIGN_LEFT;
 +      ar = MEM_callocN(sizeof(ARegion), "right aligned main region for topbar");
 +      BLI_addtail(&stopbar->regionbase, ar);
 +      ar->regiontype = RGN_TYPE_WINDOW;
 +      ar->alignment = RGN_ALIGN_RIGHT;
 +      ar = MEM_callocN(sizeof(ARegion), "center main region for topbar");
 +      BLI_addtail(&stopbar->regionbase, ar);
 +      ar->regiontype = RGN_TYPE_WINDOW;
 +
 +      return (SpaceLink *)stopbar;
 +}
 +
 +/* not spacelink itself */
 +static void topbar_free(SpaceLink *UNUSED(sl))
 +{
 +
 +}
 +
 +
 +/* spacetype; init callback */
 +static void topbar_init(struct wmWindowManager *UNUSED(wm), ScrArea *UNUSED(sa))
 +{
 +
 +}
 +
 +static SpaceLink *topbar_duplicate(SpaceLink *sl)
 +{
 +      SpaceTopBar *stopbarn = MEM_dupallocN(sl);
 +
 +      /* clear or remove stuff from old */
 +
 +      return (SpaceLink *)stopbarn;
 +}
 +
 +
 +
 +/* add handlers, stuff you only do once or on area/region changes */
 +static void topbar_main_region_init(wmWindowManager *wm, ARegion *region)
 +{
 +      wmKeyMap *keymap;
 +
 +      /* force delayed UI_view2d_region_reinit call */
 +      if (ELEM(region->alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) {
 +              region->flag |= RGN_FLAG_DYNAMIC_SIZE;
 +      }
 +      UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_HEADER, region->winx, region->winy);
 +
++      keymap = WM_keymap_ensure(wm->defaultconf, "View2D Buttons List", 0, 0);
 +      WM_event_add_keymap_handler(&region->handlers, keymap);
 +}
 +
 +static void topbar_operatortypes(void)
 +{
 +
 +}
 +
 +static void topbar_keymap(struct wmKeyConfig *UNUSED(keyconf))
 +{
 +
 +}
 +
 +/* add handlers, stuff you only do once or on area/region changes */
 +static void topbar_header_region_init(wmWindowManager *UNUSED(wm), ARegion *ar)
 +{
 +      if ((ar->alignment & ~RGN_SPLIT_PREV) == RGN_ALIGN_RIGHT) {
 +              ar->flag |= RGN_FLAG_DYNAMIC_SIZE;
 +      }
 +      ED_region_header_init(ar);
 +}
 +
 +static void topbar_main_region_listener(wmWindow *UNUSED(win), ScrArea *UNUSED(sa), ARegion *ar,
 +                                        wmNotifier *wmn, const Scene *UNUSED(scene))
 +{
 +      /* context changes */
 +      switch (wmn->category) {
 +              case NC_WM:
 +                      if (wmn->data == ND_HISTORY)
 +                              ED_region_tag_redraw(ar);
 +                      break;
 +              case NC_SCENE:
 +                      if (wmn->data == ND_MODE)
 +                              ED_region_tag_redraw(ar);
 +                      break;
 +              case NC_SPACE:
 +                      if (wmn->data == ND_SPACE_VIEW3D)
 +                              ED_region_tag_redraw(ar);
 +                      break;
 +              case NC_GPENCIL:
 +                      if (wmn->data == ND_DATA)
 +                              ED_region_tag_redraw(ar);
 +                      break;
 +      }
 +}
 +
 +static void topbar_header_listener(wmWindow *UNUSED(win), ScrArea *UNUSED(sa), ARegion *ar,
 +                                   wmNotifier *wmn, const Scene *UNUSED(scene))
 +{
 +      /* context changes */
 +      switch (wmn->category) {
 +              case NC_SCREEN:
 +                      if (wmn->data == ND_LAYER)
 +                              ED_region_tag_redraw(ar);
 +                      break;
 +              case NC_SCENE:
 +                      if (wmn->data == ND_SCENEBROWSE)
 +                              ED_region_tag_redraw(ar);
 +                      break;
 +      }
 +}
 +
 +static void topbar_header_region_message_subscribe(
 +        const struct bContext *UNUSED(C),
 +        struct WorkSpace *workspace, struct Scene *UNUSED(scene),
 +        struct bScreen *UNUSED(screen), struct ScrArea *UNUSED(sa), struct ARegion *ar,
 +        struct wmMsgBus *mbus)
 +{
 +      wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {
 +              .owner = ar,
 +              .user_data = ar,
 +              .notify = ED_region_do_msg_notify_tag_redraw,
 +      };
 +
 +      WM_msg_subscribe_rna_prop(
 +              mbus, &workspace->id, workspace,
 +              WorkSpace, tools, &msg_sub_value_region_tag_redraw);
 +}
 +
 +static void recent_files_menu_draw(const bContext *UNUSED(C), Menu *menu)
 +{
 +      struct RecentFile *recent;
 +      uiLayout *layout = menu->layout;
 +      uiLayoutSetOperatorContext(layout, WM_OP_EXEC_REGION_WIN);
 +      if (!BLI_listbase_is_empty(&G.recent_files)) {
 +              for (recent = G.recent_files.first; (recent); recent = recent->next) {
 +                      const char *file = BLI_path_basename(recent->filepath);
 +                      const int icon = BLO_has_bfile_extension(file) ? ICON_FILE_BLEND : ICON_FILE_BACKUP;
 +                      uiItemStringO(layout, file, icon, "WM_OT_open_mainfile", "filepath", recent->filepath);
 +              }
 +      }
 +      else {
 +              uiItemL(layout, IFACE_("No Recent Files"), ICON_NONE);
 +      }
 +}
 +
 +static void recent_files_menu_register(void)
 +{
 +      MenuType *mt;
 +
 +      mt = MEM_callocN(sizeof(MenuType), "spacetype info menu recent files");
 +      strcpy(mt->idname, "TOPBAR_MT_file_open_recent");
 +      strcpy(mt->label, N_("Open Recent..."));
 +      strcpy(mt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
 +      mt->draw = recent_files_menu_draw;
 +      WM_menutype_add(mt);
 +}
 +
 +
 +/* only called once, from space/spacetypes.c */
 +void ED_spacetype_topbar(void)
 +{
 +      SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype topbar");
 +      ARegionType *art;
 +
 +      st->spaceid = SPACE_TOPBAR;
 +      strncpy(st->name, "Top Bar", BKE_ST_MAXNAME);
 +
 +      st->new = topbar_new;
 +      st->free = topbar_free;
 +      st->init = topbar_init;
 +      st->duplicate = topbar_duplicate;
 +      st->operatortypes = topbar_operatortypes;
 +      st->keymap = topbar_keymap;
 +
 +      /* regions: main window */
 +      art = MEM_callocN(sizeof(ARegionType), "spacetype topbar main region");
 +      art->regionid = RGN_TYPE_WINDOW;
 +      art->init = topbar_main_region_init;
 +      art->layout = ED_region_header_layout;
 +      art->draw = ED_region_header_draw;
 +      art->listener = topbar_main_region_listener;
 +      art->prefsizex = UI_UNIT_X * 5; /* Mainly to avoid glitches */
 +      art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
 +
 +      BLI_addhead(&st->regiontypes, art);
 +
 +      /* regions: header */
 +      art = MEM_callocN(sizeof(ARegionType), "spacetype topbar header region");
 +      art->regionid = RGN_TYPE_HEADER;
 +      art->prefsizey = HEADERY;
 +      art->prefsizex = UI_UNIT_X * 5; /* Mainly to avoid glitches */
 +      art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_HEADER;
 +      art->listener = topbar_header_listener;
 +      art->message_subscribe = topbar_header_region_message_subscribe;
 +      art->init = topbar_header_region_init;
 +      art->layout = ED_region_header_layout;
 +      art->draw = ED_region_header_draw;
 +
 +      BLI_addhead(&st->regiontypes, art);
 +
 +      recent_files_menu_register();
 +
 +      BKE_spacetype_register(st);
 +}
@@@ -243,9 -238,26 +243,9 @@@ void view3d_keymap(wmKeyConfig *keyconf
        WM_keymap_add_item(keymap, "VIEW3D_OT_toolshelf", TKEY, KM_PRESS, 0, 0);
  
        /* only for region 3D window */
-       keymap = WM_keymap_find(keyconf, "3D View", SPACE_VIEW3D, 0);
+       keymap = WM_keymap_ensure(keyconf, "3D View", SPACE_VIEW3D, 0);
  
 -      /* Shift+LMB behavior first, so it has priority over KM_ANY item below. */
 -      kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_manipulator", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
 -      RNA_boolean_set(kmi->ptr, "release_confirm", true);
 -      RNA_boolean_set(kmi->ptr, "use_planar_constraint", true);
 -      RNA_boolean_set(kmi->ptr, "use_accurate", false);
 -
 -      kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_manipulator", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
 -      RNA_boolean_set(kmi->ptr, "release_confirm", true);
 -      RNA_boolean_set(kmi->ptr, "use_planar_constraint", false);
 -      RNA_boolean_set(kmi->ptr, "use_accurate", true);
 -
 -      /* Using KM_ANY here to allow holding modifiers before starting to transform. */
 -      kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_manipulator", LEFTMOUSE, KM_PRESS, KM_ANY, 0);
 -      RNA_boolean_set(kmi->ptr, "release_confirm", true);
 -      RNA_boolean_set(kmi->ptr, "use_planar_constraint", false);
 -      RNA_boolean_set(kmi->ptr, "use_accurate", false);
 -
 -      WM_keymap_verify_item(keymap, "VIEW3D_OT_cursor3d", ACTIONMOUSE, KM_PRESS, 0, 0);
 +      WM_keymap_verify_item(keymap, "VIEW3D_OT_cursor3d", ACTIONMOUSE, KM_CLICK, 0, 0);
  
        WM_keymap_verify_item(keymap, "VIEW3D_OT_rotate", MIDDLEMOUSE, KM_PRESS, 0, 0);
        WM_keymap_verify_item(keymap, "VIEW3D_OT_move", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
@@@ -4397,10 -4458,9 +4397,10 @@@ void ED_keymap_uvedit(wmKeyConfig *keyc
        wmKeyMap *keymap;
        wmKeyMapItem *kmi;
  
-       keymap = WM_keymap_find(keyconf, "UV Editor", 0, 0);
+       keymap = WM_keymap_ensure(keyconf, "UV Editor", 0, 0);
        keymap->poll = ED_operator_uvedit_can_uv_sculpt;
  
 +#ifdef USE_WM_KEYMAP_27X
        /* Uv sculpt toggle */
        kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", QKEY, KM_PRESS, 0, 0);
        RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_uv_sculpt");
index 3ba9fa7,0000000..e46cd90
mode 100644,000000..100644
--- /dev/null
@@@ -1,947 -1,0 +1,947 @@@
-       wmKeyMap *km = WM_keymap_find(config, gzgt->name, gzgt->gzmap_params.spaceid, gzgt->gzmap_params.regionid);
 +/*
 + * ***** BEGIN GPL LICENSE BLOCK *****
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * The Original Code is Copyright (C) 2014 Blender Foundation.
 + * All rights reserved.
 + *
 + * Contributor(s): Blender Foundation
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/windowmanager/gizmo/intern/wm_gizmo_group.c
 + *  \ingroup wm
 + *
 + * \name Gizmo-Group
 + *
 + * Gizmo-groups store and manage groups of gizmos. They can be
 + * attached to modal handlers and have own keymaps.
 + */
 +
 +#include <stdlib.h>
 +#include <string.h>
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_listbase.h"
 +#include "BLI_string.h"
 +#include "BLI_math.h"
 +
 +#include "BKE_context.h"
 +#include "BKE_main.h"
 +#include "BKE_report.h"
 +#include "BKE_workspace.h"
 +
 +#include "RNA_access.h"
 +#include "RNA_define.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +#include "wm_event_system.h"
 +
 +#include "ED_screen.h"
 +#include "ED_undo.h"
 +
 +/* own includes */
 +#include "wm_gizmo_wmapi.h"
 +#include "wm_gizmo_intern.h"
 +
 +#ifdef WITH_PYTHON
 +#  include "BPY_extern.h"
 +#endif
 +
 +/* Allow gizmo part's to be single click only,
 + * dragging falls back to activating their 'drag_part' action. */
 +#define USE_DRAG_DETECT
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmGizmoGroup
 + *
 + * \{ */
 +
 +/**
 + * Create a new gizmo-group from \a gzgt.
 + */
 +wmGizmoGroup *wm_gizmogroup_new_from_type(
 +        wmGizmoMap *gzmap, wmGizmoGroupType *gzgt)
 +{
 +      wmGizmoGroup *gzgroup = MEM_callocN(sizeof(*gzgroup), "gizmo-group");
 +      gzgroup->type = gzgt;
 +
 +      /* keep back-link */
 +      gzgroup->parent_gzmap = gzmap;
 +
 +      BLI_addtail(&gzmap->groups, gzgroup);
 +
 +      return gzgroup;
 +}
 +
 +void wm_gizmogroup_free(bContext *C, wmGizmoGroup *gzgroup)
 +{
 +      wmGizmoMap *gzmap = gzgroup->parent_gzmap;
 +
 +      /* Similar to WM_gizmo_unlink, but only to keep gzmap state correct,
 +       * we don't want to run callbacks. */
 +      if (gzmap->gzmap_context.highlight && gzmap->gzmap_context.highlight->parent_gzgroup == gzgroup) {
 +              wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
 +      }
 +      if (gzmap->gzmap_context.modal && gzmap->gzmap_context.modal->parent_gzgroup == gzgroup) {
 +              wm_gizmomap_modal_set(gzmap, C, gzmap->gzmap_context.modal, NULL, false);
 +      }
 +
 +      for (wmGizmo *gz = gzgroup->gizmos.first, *gz_next; gz; gz = gz_next) {
 +              gz_next = gz->next;
 +              if (gzmap->gzmap_context.select.len) {
 +                      WM_gizmo_select_unlink(gzmap, gz);
 +              }
 +              WM_gizmo_free(gz);
 +      }
 +      BLI_listbase_clear(&gzgroup->gizmos);
 +
 +#ifdef WITH_PYTHON
 +      if (gzgroup->py_instance) {
 +              /* do this first in case there are any __del__ functions or
 +               * similar that use properties */
 +              BPY_DECREF_RNA_INVALIDATE(gzgroup->py_instance);
 +      }
 +#endif
 +
 +      if (gzgroup->reports && (gzgroup->reports->flag & RPT_FREE)) {
 +              BKE_reports_clear(gzgroup->reports);
 +              MEM_freeN(gzgroup->reports);
 +      }
 +
 +      if (gzgroup->customdata_free) {
 +              gzgroup->customdata_free(gzgroup->customdata);
 +      }
 +      else {
 +              MEM_SAFE_FREE(gzgroup->customdata);
 +      }
 +
 +      BLI_remlink(&gzmap->groups, gzgroup);
 +
 +      MEM_freeN(gzgroup);
 +}
 +
 +/**
 + * Add \a gizmo to \a gzgroup and make sure its name is unique within the group.
 + */
 +void wm_gizmogroup_gizmo_register(wmGizmoGroup *gzgroup, wmGizmo *gz)
 +{
 +      BLI_assert(BLI_findindex(&gzgroup->gizmos, gz) == -1);
 +      BLI_addtail(&gzgroup->gizmos, gz);
 +      gz->parent_gzgroup = gzgroup;
 +}
 +
 +wmGizmo *wm_gizmogroup_find_intersected_gizmo(
 +        const wmGizmoGroup *gzgroup, bContext *C, const wmEvent *event,
 +        int *r_part)
 +{
 +      for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) {
 +              if (gz->type->test_select && (gz->flag & WM_GIZMO_HIDDEN) == 0) {
 +                      if ((*r_part = gz->type->test_select(C, gz, event->mval)) != -1) {
 +                              return gz;
 +                      }
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +/**
 + * Adds all gizmos of \a gzgroup that can be selected to the head of \a listbase. Added items need freeing!
 + */
 +void wm_gizmogroup_intersectable_gizmos_to_list(const wmGizmoGroup *gzgroup, ListBase *listbase)
 +{
 +      for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) {
 +              if ((gz->flag & WM_GIZMO_HIDDEN) == 0) {
 +                      if (((gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) && (gz->type->draw_select || gz->type->test_select)) ||
 +                          ((gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) == 0 && gz->type->test_select))
 +                      {
 +                              BLI_addhead(listbase, BLI_genericNodeN(gz));
 +                      }
 +              }
 +      }
 +}
 +
 +void wm_gizmogroup_ensure_initialized(wmGizmoGroup *gzgroup, const bContext *C)
 +{
 +      /* prepare for first draw */
 +      if (UNLIKELY((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0)) {
 +              gzgroup->type->setup(C, gzgroup);
 +
 +              /* Not ideal, initialize keymap here, needed for RNA runtime generated gizmos. */
 +              wmGizmoGroupType *gzgt = gzgroup->type;
 +              if (gzgt->keymap == NULL) {
 +                      wmWindowManager *wm = CTX_wm_manager(C);
 +                      wm_gizmogrouptype_setup_keymap(gzgt, wm->defaultconf);
 +                      BLI_assert(gzgt->keymap != NULL);
 +              }
 +              gzgroup->init_flag |= WM_GIZMOGROUP_INIT_SETUP;
 +      }
 +
 +      /* refresh may be called multiple times, this just ensures its called at least once before we draw. */
 +      if (UNLIKELY((gzgroup->init_flag & WM_GIZMOGROUP_INIT_REFRESH) == 0)) {
 +              if (gzgroup->type->refresh) {
 +                      gzgroup->type->refresh(C, gzgroup);
 +              }
 +              gzgroup->init_flag |= WM_GIZMOGROUP_INIT_REFRESH;
 +      }
 +}
 +
 +bool WM_gizmo_group_type_poll(const bContext *C, const struct wmGizmoGroupType *gzgt)
 +{
 +      /* If we're tagged, only use compatible. */
 +      if (gzgt->owner_id[0] != '\0') {
 +              const WorkSpace *workspace = CTX_wm_workspace(C);
 +              if (BKE_workspace_owner_id_check(workspace, gzgt->owner_id) == false) {
 +                      return false;
 +              }
 +      }
 +      /* Check for poll function, if gizmo-group belongs to an operator, also check if the operator is running. */
 +      return (!gzgt->poll || gzgt->poll(C, (wmGizmoGroupType *)gzgt));
 +}
 +
 +bool wm_gizmogroup_is_visible_in_drawstep(
 +        const wmGizmoGroup *gzgroup, const eWM_GizmoFlagMapDrawStep drawstep)
 +{
 +      switch (drawstep) {
 +              case WM_GIZMOMAP_DRAWSTEP_2D:
 +                      return (gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) == 0;
 +              case WM_GIZMOMAP_DRAWSTEP_3D:
 +                      return (gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D);
 +              default:
 +                      BLI_assert(0);
 +                      return false;
 +      }
 +}
 +
 +bool wm_gizmogroup_is_any_selected(const wmGizmoGroup *gzgroup)
 +{
 +      if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_SELECT) {
 +              for (const wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) {
 +                      if (gz->state & WM_GIZMO_STATE_SELECT) {
 +                              return true;
 +                      }
 +              }
 +      }
 +      return false;
 +}
 +
 +/** \} */
 +
 +/** \name Gizmo operators
 + *
 + * Basic operators for gizmo interaction with user configurable keymaps.
 + *
 + * \{ */
 +
 +static int gizmo_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
 +{
 +      ARegion *ar = CTX_wm_region(C);
 +      wmGizmoMap *gzmap = ar->gizmo_map;
 +      wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
 +      wmGizmo *highlight = gzmap->gzmap_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_gizmomap_deselect_all(gzmap);
 +              BLI_assert(msel->items == NULL && msel->len == 0);
 +              UNUSED_VARS_NDEBUG(msel);
 +      }
 +
 +      if (highlight) {
 +              const bool is_selected = (highlight->state & WM_GIZMO_STATE_SELECT);
 +              bool redraw = false;
 +
 +              if (toggle) {
 +                      /* toggle: deselect if already selected, else select */
 +                      deselect = is_selected;
 +              }
 +
 +              if (deselect) {
 +                      if (is_selected && WM_gizmo_select_set(gzmap, highlight, false)) {
 +                              redraw = true;
 +                      }
 +              }
 +              else if (wm_gizmo_select_and_highlight(C, gzmap, highlight)) {
 +                      redraw = true;
 +              }
 +
 +              if (redraw) {
 +                      ED_region_tag_redraw(ar);
 +              }
 +
 +              return OPERATOR_FINISHED;
 +      }
 +      else {
 +              BLI_assert(0);
 +              return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
 +      }
 +}
 +
 +void GIZMOGROUP_OT_gizmo_select(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Gizmo Select";
 +      ot->description = "Select the currently highlighted gizmo";
 +      ot->idname = "GIZMOGROUP_OT_gizmo_select";
 +
 +      /* api callbacks */
 +      ot->invoke = gizmo_select_invoke;
 +
 +      ot->flag = OPTYPE_UNDO;
 +
 +      WM_operator_properties_mouse_select(ot);
 +}
 +
 +typedef struct GizmoTweakData {
 +      wmGizmoMap *gzmap;
 +      wmGizmoGroup *gzgroup;
 +      wmGizmo *gz_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 gizmos 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 gizmo state (un-grabbs the cursor).
 +               * Ideally this workaround could be removed later. */
 +              DRAG_IDLE,
 +      } drag_state;
 +#endif
 +
 +} GizmoTweakData;
 +
 +static bool gizmo_tweak_start(
 +        bContext *C, wmGizmoMap *gzmap, wmGizmo *gz, const wmEvent *event)
 +{
 +      /* activate highlighted gizmo */
 +      wm_gizmomap_modal_set(gzmap, C, gz, event, true);
 +
 +      return (gz->state & WM_GIZMO_STATE_MODAL);
 +}
 +
 +static bool gizmo_tweak_start_and_finish(
 +        bContext *C, wmGizmoMap *gzmap, wmGizmo *gz, const wmEvent *event, bool *r_is_modal)
 +{
 +      wmGizmoOpElem *mpop = WM_gizmo_operator_get(gz, gz->highlight_part);
 +      if (r_is_modal) {
 +              *r_is_modal = false;
 +      }
 +      if (mpop && mpop->type) {
 +
 +              /* Undo/Redo */
 +              if (mpop->is_redo) {
 +                      wmWindowManager *wm = CTX_wm_manager(C);
 +                      wmOperator *op = WM_operator_last_redo(C);
 +
 +                      /* We may want to enable this, for now the gizmo can manage it's own properties. */
 +#if 0
 +                      IDP_MergeGroup(mpop->ptr.data, op->properties, false);
 +#endif
 +
 +                      WM_operator_free_all_after(wm, op);
 +                      ED_undo_pop_op(C, op);
 +              }
 +
 +              /* XXX temporary workaround for modal gizmo operator
 +               * conflicting with modal operator attached to gizmo */
 +              if (mpop->type->modal) {
 +                      /* activate highlighted gizmo */
 +                      wm_gizmomap_modal_set(gzmap, C, gz, event, true);
 +                      if (r_is_modal) {
 +                              *r_is_modal = true;
 +                      }
 +              }
 +              else {
 +                      /* Allow for 'button' gizmos, 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 gizmo_tweak_finish(bContext *C, wmOperator *op, const bool cancel, bool clear_modal)
 +{
 +      GizmoTweakData *mtweak = op->customdata;
 +      if (mtweak->gz_modal->type->exit) {
 +              mtweak->gz_modal->type->exit(C, mtweak->gz_modal, cancel);
 +      }
 +      if (clear_modal) {
 +              /* The gizmo may have been removed. */
 +              if ((BLI_findindex(&mtweak->gzmap->groups, mtweak->gzgroup) != -1) &&
 +                  (BLI_findindex(&mtweak->gzgroup->gizmos, mtweak->gz_modal) != -1))
 +              {
 +                      wm_gizmomap_modal_set(mtweak->gzmap, C, mtweak->gz_modal, NULL, false);
 +              }
 +      }
 +      MEM_freeN(mtweak);
 +}
 +
 +static int gizmo_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event)
 +{
 +      GizmoTweakData *mtweak = op->customdata;
 +      wmGizmo *gz = mtweak->gz_modal;
 +      int retval = OPERATOR_PASS_THROUGH;
 +      bool clear_modal = true;
 +
 +      if (gz == NULL) {
 +              BLI_assert(0);
 +              return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
 +      }
 +
 +#ifdef USE_DRAG_DETECT
 +      wmGizmoMap *gzmap = mtweak->gzmap;
 +      if (mtweak->drag_state == DRAG_DETECT) {
 +              if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
 +                      if (len_manhattan_v2v2_int(&event->x, gzmap->gzmap_context.event_xy) > 2) {
 +                              mtweak->drag_state = DRAG_IDLE;
 +                              gz->highlight_part = gz->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 'gizmo_tweak_invoke' */
 +                      bool is_modal = false;
 +                      if (gizmo_tweak_start_and_finish(C, gzmap, gz, event, &is_modal)) {
 +                              if (is_modal) {
 +                                      clear_modal = false;
 +                              }
 +                      }
 +                      else {
 +                              if (!gizmo_tweak_start(C, gzmap, gz, event)) {
 +                                      retval = OPERATOR_FINISHED;
 +                              }
 +                      }
 +              }
 +      }
 +      if (mtweak->drag_state == DRAG_IDLE) {
 +              if (gzmap->gzmap_context.modal != NULL) {
 +                      return OPERATOR_PASS_THROUGH;
 +              }
 +              else {
 +                      gizmo_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_GIZMO_TWEAK_PRECISE;
 +                              break;
 +                      case TWEAK_MODAL_PRECISION_OFF:
 +                              mtweak->flag &= ~WM_GIZMO_TWEAK_PRECISE;
 +                              break;
 +
 +                      case TWEAK_MODAL_SNAP_ON:
 +                              mtweak->flag |= WM_GIZMO_TWEAK_SNAP;
 +                              break;
 +                      case TWEAK_MODAL_SNAP_OFF:
 +                              mtweak->flag &= ~WM_GIZMO_TWEAK_SNAP;
 +                              break;
 +              }
 +      }
 +
 +      if (retval != OPERATOR_PASS_THROUGH) {
 +              gizmo_tweak_finish(C, op, retval != OPERATOR_FINISHED, clear_modal);
 +              return retval;
 +      }
 +
 +      /* handle gizmo */
 +      wmGizmoFnModal modal_fn = gz->custom_modal ? gz->custom_modal : gz->type->modal;
 +      if (modal_fn) {
 +              int modal_retval = modal_fn(C, gz, event, mtweak->flag);
 +
 +              if ((modal_retval & OPERATOR_RUNNING_MODAL) == 0) {
 +                      gizmo_tweak_finish(C, op, (modal_retval & OPERATOR_CANCELLED) != 0, true);
 +                      return OPERATOR_FINISHED;
 +              }
 +
 +              /* Ugly hack to send gizmo events */
 +              ((wmEvent *)event)->type = EVT_GIZMO_UPDATE;
 +      }
 +
 +      /* always return PASS_THROUGH so modal handlers
 +       * with gizmos attached can update */
 +      BLI_assert(retval == OPERATOR_PASS_THROUGH);
 +      return OPERATOR_PASS_THROUGH;
 +}
 +
 +static int gizmo_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 +{
 +      ARegion *ar = CTX_wm_region(C);
 +      wmGizmoMap *gzmap = ar->gizmo_map;
 +      wmGizmo *gz = gzmap->gzmap_context.highlight;
 +
 +      /* Needed for single click actions which don't enter modal state. */
 +      WM_tooltip_clear(C, CTX_wm_window(C));
 +
 +      if (!gz) {
 +              /* 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(gz->drag_part, -1, gz->highlight_part);
 +#endif
 +
 +      if (use_drag_fallback == false) {
 +              if (gizmo_tweak_start_and_finish(C, gzmap, gz, event, NULL)) {
 +                      return OPERATOR_FINISHED;
 +              }
 +      }
 +
 +      bool use_drag_detect = false;
 +#ifdef USE_DRAG_DETECT
 +      if (use_drag_fallback) {
 +              wmGizmoOpElem *mpop = WM_gizmo_operator_get(gz, gz->highlight_part);
 +              if (mpop && mpop->type) {
 +                      if (mpop->type->modal == NULL) {
 +                              use_drag_detect = true;
 +                      }
 +              }
 +      }
 +#endif
 +
 +      if (use_drag_detect == false) {
 +              if (!gizmo_tweak_start(C, gzmap, gz, event)) {
 +                      /* failed to start */
 +                      return OPERATOR_PASS_THROUGH;
 +              }
 +      }
 +
 +      GizmoTweakData *mtweak = MEM_mallocN(sizeof(GizmoTweakData), __func__);
 +
 +      mtweak->init_event = WM_userdef_event_type_from_keymap_type(event->type);
 +      mtweak->gz_modal = gzmap->gzmap_context.highlight;
 +      mtweak->gzgroup = mtweak->gz_modal->parent_gzgroup;
 +      mtweak->gzmap = gzmap;
 +      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 GIZMOGROUP_OT_gizmo_tweak(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Gizmo Tweak";
 +      ot->description = "Tweak the active gizmo";
 +      ot->idname = "GIZMOGROUP_OT_gizmo_tweak";
 +
 +      /* api callbacks */
 +      ot->invoke = gizmo_tweak_invoke;
 +      ot->modal = gizmo_tweak_modal;
 +
 +      /* TODO(campbell) This causes problems tweaking settings for operators,
 +       * need to find a way to support this. */
 +#if 0
 +      ot->flag = OPTYPE_UNDO;
 +#endif
 +}
 +
 +/** \} */
 +
 +
 +static wmKeyMap *gizmogroup_tweak_modal_keymap(wmKeyConfig *keyconf, const char *gzgroupname)
 +{
 +      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", gzgroupname);
 +      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, "GIZMOGROUP_OT_gizmo_tweak");
 +
 +      return keymap;
 +}
 +
 +/**
 + * Common default keymap for gizmo groups
 + */
 +wmKeyMap *WM_gizmogroup_keymap_common(
 +        const wmGizmoGroupType *gzgt, wmKeyConfig *config)
 +{
 +      /* Use area and region id since we might have multiple gizmos with the same name in different areas/regions */
-       wmKeyMap *km = WM_keymap_find(config, gzgt->name, gzgt->gzmap_params.spaceid, gzgt->gzmap_params.regionid);
++      wmKeyMap *km = WM_keymap_ensure(config, gzgt->name, gzgt->gzmap_params.spaceid, gzgt->gzmap_params.regionid);
 +
 +      WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", LEFTMOUSE, KM_PRESS, KM_ANY, 0);
 +      gizmogroup_tweak_modal_keymap(config, gzgt->name);
 +
 +      return km;
 +}
 +
 +/**
 + * Variation of #WM_gizmogroup_keymap_common but with keymap items for selection
 + */
 +wmKeyMap *WM_gizmogroup_keymap_common_select(
 +        const wmGizmoGroupType *gzgt, wmKeyConfig *config)
 +{
 +      /* Use area and region id since we might have multiple gizmos with the same name in different areas/regions */
++      wmKeyMap *km = WM_keymap_ensure(config, gzgt->name, gzgt->gzmap_params.spaceid, gzgt->gzmap_params.regionid);
 +
 +      WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", ACTIONMOUSE, KM_PRESS, KM_ANY, 0);
 +      WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", EVT_TWEAK_S, KM_ANY, 0, 0);
 +      gizmogroup_tweak_modal_keymap(config, gzgt->name);
 +
 +      wmKeyMapItem *kmi = WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_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, "GIZMOGROUP_OT_gizmo_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;
 +}
 +
 +/** \} */ /* wmGizmoGroup */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmGizmoGroupType
 + *
 + * \{ */
 +
 +struct wmGizmoGroupTypeRef *WM_gizmomaptype_group_find_ptr(
 +        struct wmGizmoMapType *gzmap_type,
 +        const wmGizmoGroupType *gzgt)
 +{
 +      /* could use hash lookups as operator types do, for now simple search. */
 +      for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first;
 +           gzgt_ref;
 +           gzgt_ref = gzgt_ref->next)
 +      {
 +              if (gzgt_ref->type == gzgt) {
 +                      return gzgt_ref;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +struct wmGizmoGroupTypeRef *WM_gizmomaptype_group_find(
 +        struct wmGizmoMapType *gzmap_type,
 +        const char *idname)
 +{
 +      /* could use hash lookups as operator types do, for now simple search. */
 +      for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first;
 +           gzgt_ref;
 +           gzgt_ref = gzgt_ref->next)
 +      {
 +              if (STREQ(idname, gzgt_ref->type->idname)) {
 +                      return gzgt_ref;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +/**
 + * Use this for registering gizmos on startup. For runtime, use #WM_gizmomaptype_group_link_runtime.
 + */
 +wmGizmoGroupTypeRef *WM_gizmomaptype_group_link(
 +        wmGizmoMapType *gzmap_type, const char *idname)
 +{
 +      wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
 +      BLI_assert(gzgt != NULL);
 +      return WM_gizmomaptype_group_link_ptr(gzmap_type, gzgt);
 +}
 +
 +wmGizmoGroupTypeRef *WM_gizmomaptype_group_link_ptr(
 +        wmGizmoMapType *gzmap_type, wmGizmoGroupType *gzgt)
 +{
 +      wmGizmoGroupTypeRef *gzgt_ref = MEM_callocN(sizeof(wmGizmoGroupTypeRef), "gizmo-group-ref");
 +      gzgt_ref->type = gzgt;
 +      BLI_addtail(&gzmap_type->grouptype_refs, gzgt_ref);
 +      return gzgt_ref;
 +}
 +
 +void WM_gizmomaptype_group_init_runtime_keymap(
 +        const Main *bmain,
 +        wmGizmoGroupType *gzgt)
 +{
 +      /* init keymap - on startup there's an extra call to init keymaps for 'permanent' gizmo-groups */
 +      wm_gizmogrouptype_setup_keymap(gzgt, ((wmWindowManager *)bmain->wm.first)->defaultconf);
 +}
 +
 +void WM_gizmomaptype_group_init_runtime(
 +        const Main *bmain, wmGizmoMapType *gzmap_type,
 +        wmGizmoGroupType *gzgt)
 +{
 +      /* now create a gizmo 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) {
 +                                      wmGizmoMap *gzmap = ar->gizmo_map;
 +                                      if (gzmap && gzmap->type == gzmap_type) {
 +                                              wm_gizmogroup_new_from_type(gzmap, gzgt);
 +
 +                                              /* just add here, drawing will occur on next update */
 +                                              wm_gizmomap_highlight_set(gzmap, NULL, NULL, 0);
 +                                              ED_region_tag_redraw(ar);
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +
 +/**
 + * Unlike #WM_gizmomaptype_group_unlink this doesn't maintain correct state, simply free.
 + */
 +void WM_gizmomaptype_group_free(wmGizmoGroupTypeRef *gzgt_ref)
 +{
 +      MEM_freeN(gzgt_ref);
 +}
 +
 +void WM_gizmomaptype_group_unlink(
 +        bContext *C, Main *bmain, wmGizmoMapType *gzmap_type,
 +        const wmGizmoGroupType *gzgt)
 +{
 +      /* 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) {
 +                                      wmGizmoMap *gzmap = ar->gizmo_map;
 +                                      if (gzmap && gzmap->type == gzmap_type) {
 +                                              wmGizmoGroup *gzgroup, *gzgroup_next;
 +                                              for (gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup_next) {
 +                                                      gzgroup_next = gzgroup->next;
 +                                                      if (gzgroup->type == gzgt) {
 +                                                              BLI_assert(gzgroup->parent_gzmap == gzmap);
 +                                                              wm_gizmogroup_free(C, gzgroup);
 +                                                              ED_region_tag_redraw(ar);
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      /* Free types. */
 +      wmGizmoGroupTypeRef *gzgt_ref = WM_gizmomaptype_group_find_ptr(gzmap_type, gzgt);
 +      if (gzgt_ref) {
 +              BLI_remlink(&gzmap_type->grouptype_refs, gzgt_ref);
 +              WM_gizmomaptype_group_free(gzgt_ref);
 +      }
 +
 +      /* Note, we may want to keep this keymap for editing */
 +      WM_keymap_remove(gzgt->keyconf, gzgt->keymap);
 +
 +      BLI_assert(WM_gizmomaptype_group_find_ptr(gzmap_type, gzgt) == NULL);
 +}
 +
 +void wm_gizmogrouptype_setup_keymap(
 +        wmGizmoGroupType *gzgt, wmKeyConfig *keyconf)
 +{
 +      /* Use flag since setup_keymap may return NULL,
 +       * in that case we better not keep calling it. */
 +      if (gzgt->type_update_flag & WM_GIZMOMAPTYPE_KEYMAP_INIT) {
 +              gzgt->keymap = gzgt->setup_keymap(gzgt, keyconf);
 +              gzgt->keyconf = keyconf;
 +              gzgt->type_update_flag &= ~WM_GIZMOMAPTYPE_KEYMAP_INIT;
 +      }
 +}
 +
 +/** \} */ /* wmGizmoGroupType */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name High Level Add/Remove API
 + *
 + * For use directly from operators & RNA registration.
 + *
 + * \note In context of gizmo API these names are a bit misleading,
 + * but for general use terms its OK.
 + * `WM_gizmo_group_type_add` would be more correctly called:
 + * `WM_gizmomaptype_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_gizmo_group_type_add_ptr_ex(
 +        wmGizmoGroupType *gzgt,
 +        wmGizmoMapType *gzmap_type)
 +{
 +      WM_gizmomaptype_group_link_ptr(gzmap_type, gzgt);
 +
 +      WM_gizmoconfig_update_tag_init(gzmap_type, gzgt);
 +}
 +void WM_gizmo_group_type_add_ptr(
 +        wmGizmoGroupType *gzgt)
 +{
 +      wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
 +      WM_gizmo_group_type_add_ptr_ex(gzgt, gzmap_type);
 +}
 +void WM_gizmo_group_type_add(const char *idname)
 +{
 +      wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
 +      BLI_assert(gzgt != NULL);
 +      WM_gizmo_group_type_add_ptr(gzgt);
 +}
 +
 +void WM_gizmo_group_type_ensure_ptr_ex(
 +        wmGizmoGroupType *gzgt,
 +        wmGizmoMapType *gzmap_type)
 +{
 +      wmGizmoGroupTypeRef *gzgt_ref = WM_gizmomaptype_group_find_ptr(gzmap_type, gzgt);
 +      if (gzgt_ref == NULL) {
 +              WM_gizmo_group_type_add_ptr_ex(gzgt, gzmap_type);
 +      }
 +}
 +void WM_gizmo_group_type_ensure_ptr(
 +        wmGizmoGroupType *gzgt)
 +{
 +      wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
 +      WM_gizmo_group_type_ensure_ptr_ex(gzgt, gzmap_type);
 +}
 +void WM_gizmo_group_type_ensure(const char *idname)
 +{
 +      wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
 +      BLI_assert(gzgt != NULL);
 +      WM_gizmo_group_type_ensure_ptr(gzgt);
 +}
 +
 +void WM_gizmo_group_type_remove_ptr_ex(
 +        struct Main *bmain, wmGizmoGroupType *gzgt,
 +        wmGizmoMapType *gzmap_type)
 +{
 +      WM_gizmomaptype_group_unlink(NULL, bmain, gzmap_type, gzgt);
 +      WM_gizmogrouptype_free_ptr(gzgt);
 +}
 +void WM_gizmo_group_type_remove_ptr(
 +        struct Main *bmain, wmGizmoGroupType *gzgt)
 +{
 +      wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
 +      WM_gizmo_group_type_remove_ptr_ex(bmain, gzgt, gzmap_type);
 +}
 +void WM_gizmo_group_type_remove(struct Main *bmain, const char *idname)
 +{
 +      wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
 +      BLI_assert(gzgt != NULL);
 +      WM_gizmo_group_type_remove_ptr(bmain, gzgt);
 +}
 +
 +/* delayed versions */
 +
 +void WM_gizmo_group_type_unlink_delayed_ptr_ex(
 +        wmGizmoGroupType *gzgt,
 +        wmGizmoMapType *gzmap_type)
 +{
 +      WM_gizmoconfig_update_tag_remove(gzmap_type, gzgt);
 +}
 +
 +void WM_gizmo_group_type_unlink_delayed_ptr(
 +        wmGizmoGroupType *gzgt)
 +{
 +      wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
 +      WM_gizmo_group_type_unlink_delayed_ptr_ex(gzgt, gzmap_type);
 +}
 +
 +void WM_gizmo_group_type_unlink_delayed(const char *idname)
 +{
 +      wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
 +      BLI_assert(gzgt != NULL);
 +      WM_gizmo_group_type_unlink_delayed_ptr(gzgt);
 +}
 +
 +/** \} */
index d0f6ab9,0000000..2c0d0ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,1235 -1,0 +1,1235 @@@
-       WM_keymap_find(keyconf, "Gizmos", 0, 0);
 +/*
 + * ***** BEGIN GPL LICENSE BLOCK *****
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * The Original Code is Copyright (C) 2014 Blender Foundation.
 + * All rights reserved.
 + *
 + * Contributor(s): Blender Foundation
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/windowmanager/gizmo/intern/wm_gizmo_map.c
 + *  \ingroup wm
 + */
 +
 +#include <string.h>
 +
 +#include "BLI_listbase.h"
 +#include "BLI_math.h"
 +#include "BLI_rect.h"
 +#include "BLI_string.h"
 +#include "BLI_ghash.h"
 +
 +#include "BKE_context.h"
 +#include "BKE_global.h"
 +
 +#include "ED_screen.h"
 +#include "ED_select_utils.h"
 +#include "ED_view3d.h"
 +
 +#include "GPU_glew.h"
 +#include "GPU_matrix.h"
 +#include "GPU_select.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +#include "wm_event_system.h"
 +
 +/* for tool-tips */
 +#include "UI_interface.h"
 +
 +#include "DEG_depsgraph.h"
 +
 +/* own includes */
 +#include "wm_gizmo_wmapi.h"
 +#include "wm_gizmo_intern.h"
 +
 +/**
 + * Store all gizmo-maps here. Anyone who wants to register a gizmo for a certain
 + * area type can query the gizmo-map to do so.
 + */
 +static ListBase gizmomaptypes = {NULL, NULL};
 +
 +/**
 + * Update when gizmo-map types change.
 + */
 +/* so operator removal can trigger update */
 +typedef enum eWM_GizmoFlagGroupTypeGlobalFlag {
 +      WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0),
 +      WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1),
 +} eWM_GizmoFlagGroupTypeGlobalFlag;
 +
 +static eWM_GizmoFlagGroupTypeGlobalFlag wm_gzmap_type_update_flag = 0;
 +
 +/**
 + * Gizmo-map update tagging.
 + */
 +enum {
 +      /** #gizmomap_prepare_drawing has run */
 +      GIZMOMAP_IS_PREPARE_DRAW = (1 << 0),
 +      GIZMOMAP_IS_REFRESH_CALLBACK = (1 << 1),
 +};
 +
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmGizmoMap Selection Array API
 + *
 + * Just handle ``wm_gizmomap_select_array_*``, not flags or callbacks.
 + *
 + * \{ */
 +
 +static void wm_gizmomap_select_array_ensure_len_alloc(wmGizmoMap *gzmap, int len)
 +{
 +      wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
 +      if (len <= msel->len_alloc) {
 +              return;
 +      }
 +      msel->items = MEM_reallocN(msel->items, sizeof(*msel->items) * len);
 +      msel->len_alloc = len;
 +}
 +
 +void wm_gizmomap_select_array_clear(wmGizmoMap *gzmap)
 +{
 +      wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
 +      MEM_SAFE_FREE(msel->items);
 +      msel->len = 0;
 +      msel->len_alloc = 0;
 +}
 +
 +void wm_gizmomap_select_array_shrink(wmGizmoMap *gzmap, int len_subtract)
 +{
 +      wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
 +      msel->len -= len_subtract;
 +      if (msel->len <= 0) {
 +              wm_gizmomap_select_array_clear(gzmap);
 +      }
 +      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_gizmomap_select_array_push_back(wmGizmoMap *gzmap, wmGizmo *gz)
 +{
 +      wmGizmoMapSelectState *msel = &gzmap->gzmap_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++] = gz;
 +}
 +
 +void wm_gizmomap_select_array_remove(wmGizmoMap *gzmap, wmGizmo *gz)
 +{
 +      wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
 +      /* remove gizmo from selected_gizmos array */
 +      for (int i = 0; i < msel->len; i++) {
 +              if (msel->items[i] == gz) {
 +                      for (int j = i; j < (msel->len - 1); j++) {
 +                              msel->items[j] = msel->items[j + 1];
 +                      }
 +                      wm_gizmomap_select_array_shrink(gzmap, 1);
 +                      break;
 +              }
 +      }
 +
 +}
 +
 +/** \} */
 +
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmGizmoMap
 + *
 + * \{ */
 +
 +/**
 + * Creates a gizmo-map with all registered gizmos for that type
 + */
 +wmGizmoMap *WM_gizmomap_new_from_type(
 +        const struct wmGizmoMapType_Params *gzmap_params)
 +{
 +      wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(gzmap_params);
 +      wmGizmoMap *gzmap;
 +
 +      gzmap = MEM_callocN(sizeof(wmGizmoMap), "GizmoMap");
 +      gzmap->type = gzmap_type;
 +      WM_gizmomap_tag_refresh(gzmap);
 +
 +      /* create all gizmo-groups for this gizmo-map. We may create an empty one
 +       * too in anticipation of gizmos from operators etc */
 +      for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first; gzgt_ref; gzgt_ref = gzgt_ref->next) {
 +              wm_gizmogroup_new_from_type(gzmap, gzgt_ref->type);
 +      }
 +
 +      return gzmap;
 +}
 +
 +void wm_gizmomap_remove(wmGizmoMap *gzmap)
 +{
 +      /* Clear first so further calls don't waste time trying to maintain correct array state. */
 +      wm_gizmomap_select_array_clear(gzmap);
 +
 +      for (wmGizmoGroup *gzgroup = gzmap->groups.first, *gzgroup_next; gzgroup; gzgroup = gzgroup_next) {
 +              gzgroup_next = gzgroup->next;
 +              BLI_assert(gzgroup->parent_gzmap == gzmap);
 +              wm_gizmogroup_free(NULL, gzgroup);
 +      }
 +      BLI_assert(BLI_listbase_is_empty(&gzmap->groups));
 +
 +      MEM_freeN(gzmap);
 +}
 +
 +
 +wmGizmoGroup *WM_gizmomap_group_find(
 +        struct wmGizmoMap *gzmap,
 +        const char *idname)
 +{
 +      wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
 +      if (gzgt) {
 +              return WM_gizmomap_group_find_ptr(gzmap, gzgt);
 +      }
 +      return NULL;
 +}
 +
 +wmGizmoGroup *WM_gizmomap_group_find_ptr(
 +        struct wmGizmoMap *gzmap,
 +        const struct wmGizmoGroupType *gzgt)
 +{
 +      for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
 +              if (gzgroup->type == gzgt) {
 +                      return gzgroup;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +const ListBase *WM_gizmomap_group_list(wmGizmoMap *gzmap)
 +{
 +      return &gzmap->groups;
 +}
 +
 +bool WM_gizmomap_is_any_selected(const wmGizmoMap *gzmap)
 +{
 +      return gzmap->gzmap_context.select.len != 0;
 +}
 +
 +/**
 + * \note We could use a callback to define bounds, for now just use matrix location.
 + */
 +bool WM_gizmomap_minmax(
 +        const wmGizmoMap *gzmap, bool UNUSED(use_hidden), bool use_select,
 +        float r_min[3], float r_max[3])
 +{
 +      if (use_select) {
 +              int i;
 +              for (i = 0; i < gzmap->gzmap_context.select.len; i++) {
 +                      minmax_v3v3_v3(r_min, r_max, gzmap->gzmap_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) gizmos in \a gzmap
 + *
 + * \param poll  Polling function for excluding gizmos.
 + * \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_gizmomap_gizmo_hash_new(
 +        const bContext *C, wmGizmoMap *gzmap,
 +        bool (*poll)(const wmGizmo *, void *),
 +        void *data, const bool include_hidden)
 +{
 +      GHash *hash = BLI_ghash_ptr_new(__func__);
 +
 +      /* collect gizmos */
 +      for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
 +              if (WM_gizmo_group_type_poll(C, gzgroup->type)) {
 +                      for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) {
 +                              if ((include_hidden || (gz->flag & WM_GIZMO_HIDDEN) == 0) &&
 +                                  (!poll || poll(gz, data)))
 +                              {
 +                                      BLI_ghash_insert(hash, gz, gz);
 +                              }
 +                      }
 +              }
 +      }
 +
 +      return hash;
 +}
 +
 +void WM_gizmomap_tag_refresh(wmGizmoMap *gzmap)
 +{
 +      if (gzmap) {
 +              /* We might want only to refresh some, for tag all steps. */
 +              for (int i = 0; i < WM_GIZMOMAP_DRAWSTEP_MAX; i++) {
 +                      gzmap->update_flag[i] |= (
 +                              GIZMOMAP_IS_PREPARE_DRAW |
 +                              GIZMOMAP_IS_REFRESH_CALLBACK);
 +              }
 +      }
 +}
 +
 +static bool gizmo_prepare_drawing(
 +        wmGizmoMap *gzmap, wmGizmo *gz,
 +        const bContext *C, ListBase *draw_gizmos,
 +        const eWM_GizmoFlagMapDrawStep drawstep)
 +{
 +      int do_draw = wm_gizmo_is_visible(gz);
 +      if (do_draw == 0) {
 +              /* skip */
 +      }
 +      else {
 +              /* Ensure we get RNA updates */
 +              if (do_draw & WM_GIZMO_IS_VISIBLE_UPDATE) {
 +                      /* hover gizmos need updating, even if we don't draw them */
 +                      wm_gizmo_update(gz, C, (gzmap->update_flag[drawstep] & GIZMOMAP_IS_PREPARE_DRAW) != 0);
 +              }
 +              if (do_draw & WM_GIZMO_IS_VISIBLE_DRAW) {
 +                      BLI_addhead(draw_gizmos, BLI_genericNodeN(gz));
 +              }
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +/**
 + * Update gizmos of \a gzmap to prepare for drawing. Adds all gizmos that
 + * should be drawn to list \a draw_gizmos, note that added items need freeing.
 + */
 +static void gizmomap_prepare_drawing(
 +        wmGizmoMap *gzmap, const bContext *C, ListBase *draw_gizmos,
 +        const eWM_GizmoFlagMapDrawStep drawstep)
 +{
 +      if (!gzmap || BLI_listbase_is_empty(&gzmap->groups))
 +              return;
 +      wmGizmo *gz_modal = gzmap->gzmap_context.modal;
 +
 +      /* only active gizmo needs updating */
 +      if (gz_modal) {
 +              if ((gz_modal->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL) == 0) {
 +                      if (wm_gizmogroup_is_visible_in_drawstep(gz_modal->parent_gzgroup, drawstep)) {
 +                              if (gizmo_prepare_drawing(gzmap, gz_modal, C, draw_gizmos, drawstep)) {
 +                                      gzmap->update_flag[drawstep] &= ~GIZMOMAP_IS_PREPARE_DRAW;
 +                              }
 +                      }
 +                      /* don't draw any other gizmos */
 +                      return;
 +              }
 +      }
 +
 +      for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
 +              /* check group visibility - drawstep first to avoid unnecessary call of group poll callback */
 +              if (!wm_gizmogroup_is_visible_in_drawstep(gzgroup, drawstep) ||
 +                  !WM_gizmo_group_type_poll(C, gzgroup->type))
 +              {
 +                      continue;
 +              }
 +
 +              /* needs to be initialized on first draw */
 +              /* XXX weak: Gizmo-group may skip refreshing if it's invisible (map gets untagged nevertheless) */
 +              if (gzmap->update_flag[drawstep] & GIZMOMAP_IS_REFRESH_CALLBACK) {
 +                      /* force refresh again. */
 +                      gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH;
 +              }
 +              /* Calls `setup`, `setup_keymap` and `refresh` if they're defined. */
 +              wm_gizmogroup_ensure_initialized(gzgroup, C);
 +
 +              /* prepare drawing */
 +              if (gzgroup->type->draw_prepare) {
 +                      gzgroup->type->draw_prepare(C, gzgroup);
 +              }
 +
 +              for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) {
 +                      gizmo_prepare_drawing(gzmap, gz, C, draw_gizmos, drawstep);
 +              }
 +      }
 +
 +      gzmap->update_flag[drawstep] &=
 +              ~(GIZMOMAP_IS_REFRESH_CALLBACK |
 +                GIZMOMAP_IS_PREPARE_DRAW);
 +}
 +
 +/**
 + * Draw all visible gizmos in \a gzmap.
 + * Uses global draw_gizmos listbase.
 + */
 +static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBase *draw_gizmos)
 +{
 +      /* Can be empty if we're dynamically added and removed. */
 +      if ((gzmap == NULL) || BLI_listbase_is_empty(&gzmap->groups)) {
 +              return;
 +      }
 +
 +      /* TODO this will need it own shader probably? don't think it can be handled from that point though. */
 +/*    const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0; */
 +
 +      bool is_depth_prev = false;
 +
 +      /* draw_gizmos contains all visible gizmos - draw them */
 +      for (LinkData *link = draw_gizmos->first, *link_next; link; link = link_next) {
 +              wmGizmo *gz = link->data;
 +              link_next = link->next;
 +
 +              bool is_depth = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DEPTH_3D) != 0;
 +
 +              /* Weak! since we don't 100% support depth yet (select ignores depth) always show highlighted */
 +              if (is_depth && (gz->state & WM_GIZMO_STATE_HIGHLIGHT)) {
 +                      is_depth = false;
 +              }
 +
 +              if (is_depth == is_depth_prev) {
 +                      /* pass */
 +              }
 +              else {
 +                      if (is_depth) {
 +                              glEnable(GL_DEPTH_TEST);
 +                      }
 +                      else {
 +                              glDisable(GL_DEPTH_TEST);
 +                      }
 +                      is_depth_prev = is_depth;
 +              }
 +
 +              /* XXX force AntiAlias Gizmos. */
 +              glEnable(GL_LINE_SMOOTH);
 +              glEnable(GL_POLYGON_SMOOTH);
 +
 +              gz->type->draw(C, gz);
 +
 +              glDisable(GL_LINE_SMOOTH);
 +              glDisable(GL_POLYGON_SMOOTH);
 +
 +              /* free/remove gizmo link after drawing */
 +              BLI_freelinkN(draw_gizmos, link);
 +      }
 +
 +      if (is_depth_prev) {
 +              glDisable(GL_DEPTH_TEST);
 +      }
 +}
 +
 +void WM_gizmomap_draw(
 +        wmGizmoMap *gzmap, const bContext *C,
 +        const eWM_GizmoFlagMapDrawStep drawstep)
 +{
 +      if (!WM_gizmo_context_check_drawstep(C, drawstep)) {
 +              return;
 +      }
 +
 +      ListBase draw_gizmos = {NULL};
 +
 +      gizmomap_prepare_drawing(gzmap, C, &draw_gizmos, drawstep);
 +      gizmos_draw_list(gzmap, C, &draw_gizmos);
 +      BLI_assert(BLI_listbase_is_empty(&draw_gizmos));
 +}
 +
 +static void gizmo_draw_select_3D_loop(
 +        const bContext *C, ListBase *visible_gizmos,
 +        const wmGizmo *gz_stop)
 +{
 +      int select_id = 0;
 +      wmGizmo *gz;
 +
 +      /* TODO(campbell): this depends on depth buffer being written to, currently broken for the 3D view. */
 +      bool is_depth_prev = false;
 +      bool is_depth_skip_prev = false;
 +
 +      for (LinkData *link = visible_gizmos->first; link; link = link->next, select_id++) {
 +              gz = link->data;
 +              if (gz == gz_stop) {
 +                      break;
 +              }
 +              if (gz->type->draw_select == NULL) {
 +                      continue;
 +              }
 +
 +              bool is_depth = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_DEPTH_3D) != 0;
 +              if (is_depth == is_depth_prev) {
 +                      /* pass */
 +              }
 +              else {
 +                      if (is_depth) {
 +                              glEnable(GL_DEPTH_TEST);
 +                      }
 +                      else {
 +                              glDisable(GL_DEPTH_TEST);
 +                      }
 +                      is_depth_prev = is_depth;
 +              }
 +              bool is_depth_skip = (gz->flag & WM_GIZMO_SELECT_BACKGROUND) != 0;
 +              if (is_depth_skip == is_depth_skip_prev) {
 +                      /* pass */
 +              }
 +              else {
 +                      glDepthMask(!is_depth_skip);
 +                      is_depth_skip_prev = is_depth_skip;
 +              }
 +
 +              /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected gizmo part id */
 +
 +              gz->type->draw_select(C, gz, select_id << 8);
 +      }
 +
 +      if (is_depth_prev) {
 +              glDisable(GL_DEPTH_TEST);
 +      }
 +      if (is_depth_skip_prev) {
 +              glDepthMask(true);
 +      }
 +}
 +
 +static int gizmo_find_intersected_3d_intern(
 +        ListBase *visible_gizmos, const bContext *C, const int co[2],
 +        const int hotspot, const wmGizmo *gz_stop)
 +{
 +      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 gizmos. */
 +      GLuint buffer[MAXPICKBUF];
 +      short hits;
 +      const bool do_passes = GPU_select_query_check_active();
 +
 +      BLI_rcti_init_pt_radius(&rect, co, hotspot);
 +
 +      ED_view3d_draw_setup_view(CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, &rect);
 +
 +      if (do_passes)
 +              GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
 +      else
 +              GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_ALL, 0);
 +      /* do the drawing */
 +      gizmo_draw_select_3D_loop(C, visible_gizmos, gz_stop);
 +
 +      hits = GPU_select_end();
 +
 +      if (do_passes && (hits > 0)) {
 +              GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
 +              gizmo_draw_select_3D_loop(C, visible_gizmos, gz_stop);
 +              GPU_select_end();
 +      }
 +
 +      ED_view3d_draw_setup_view(CTX_wm_window(C), CTX_data_depsgraph(C), CTX_data_scene(C), ar, v3d, NULL, NULL, NULL);
 +
 +      const GLuint *hit_near = GPU_select_buffer_near(buffer, hits);
 +
 +      return hit_near ? hit_near[3] : -1;
 +}
 +
 +/**
 + * Try to find a 3D gizmo at screen-space coordinate \a co. Uses OpenGL picking.
 + */
 +static wmGizmo *gizmo_find_intersected_3d(
 +        bContext *C, const int co[2], ListBase *visible_gizmos,
 +        int *r_part)
 +{
 +      wmGizmo *result = NULL;
 +      int hit = -1;
 +
 +      *r_part = 0;
 +
 +      /* set up view matrices */
 +      view3d_operator_needs_opengl(C);
 +
 +      /* Search for 3D gizmo's that use the 2D callback for checking intersections. */
 +      bool has_3d = false;
 +      {
 +              int select_id = 0;
 +              for (LinkData *link = visible_gizmos->first; link; link = link->next, select_id++) {
 +                      wmGizmo *gz = link->data;
 +                      if (gz->type->test_select) {
 +                              if ((*r_part = gz->type->test_select(C, gz, co)) != -1) {
 +                                      hit = select_id;
 +                                      result = gz;
 +                                      break;
 +                              }
 +                      }
 +                      else {
 +                              has_3d = true;
 +                      }
 +              }
 +      }
 +
 +      /* Search for 3D intersections if they're before 2D that have been found (if any).
 +       * This way we always use the first hit. */
 +      if (has_3d) {
 +              const int hotspot_radii[] = {
 +                      3 * U.pixelsize,
 +                      /* This runs on mouse move, careful doing too many tests! */
 +                      10 * U.pixelsize,
 +              };
 +              for (int i = 0; i < ARRAY_SIZE(hotspot_radii); i++) {
 +                      hit = gizmo_find_intersected_3d_intern(visible_gizmos, C, co, hotspot_radii[i], result);
 +                      if (hit != -1) {
 +                              break;
 +                      }
 +              }
 +
 +              if (hit != -1) {
 +                      LinkData *link = BLI_findlink(visible_gizmos, hit >> 8);
 +                      if (link != NULL) {
 +                              *r_part = hit & 255;
 +                              result = link->data;
 +                      }
 +                      else {
 +                              /* All gizmos 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 gizmo under the mouse position. 2D intersections have priority over
 + * 3D ones (could check for smallest screen-space distance but not needed right now).
 + */
 +wmGizmo *wm_gizmomap_highlight_find(
 +        wmGizmoMap *gzmap, bContext *C, const wmEvent *event,
 +        int *r_part)
 +{
 +      wmGizmo *gz = NULL;
 +      ListBase visible_3d_gizmos = {NULL};
 +      bool do_step[WM_GIZMOMAP_DRAWSTEP_MAX];
 +
 +      for (int i = 0; i < ARRAY_SIZE(do_step); i++) {
 +              do_step[i] = WM_gizmo_context_check_drawstep(C, i);
 +      }
 +
 +      for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->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 gizmos simple. */
 +              if ((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0) {
 +                      continue;
 +              }
 +
 +              if (WM_gizmo_group_type_poll(C, gzgroup->type)) {
 +                      eWM_GizmoFlagMapDrawStep step;
 +                      if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) {
 +                              step = WM_GIZMOMAP_DRAWSTEP_3D;
 +                      }
 +                      else {
 +                              step = WM_GIZMOMAP_DRAWSTEP_2D;
 +                      }
 +
 +                      if (do_step[step]) {
 +                              if ((gzmap->update_flag[step] & GIZMOMAP_IS_REFRESH_CALLBACK) &&
 +                                  (gzgroup->type->refresh != NULL))
 +                              {
 +                                      gzgroup->type->refresh(C, gzgroup);
 +                                      /* cleared below */
 +                              }
 +                              if (step == WM_GIZMOMAP_DRAWSTEP_3D) {
 +                                      wm_gizmogroup_intersectable_gizmos_to_list(gzgroup, &visible_3d_gizmos);
 +                              }
 +                              else if (step == WM_GIZMOMAP_DRAWSTEP_2D) {
 +                                      if ((gz = wm_gizmogroup_find_intersected_gizmo(gzgroup, C, event, r_part))) {
 +                                              break;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!BLI_listbase_is_empty(&visible_3d_gizmos)) {
 +              /* 2D gizmos get priority. */
 +              if (gz == NULL) {
 +                      gz = gizmo_find_intersected_3d(C, event->mval, &visible_3d_gizmos, r_part);
 +              }
 +              BLI_freelistN(&visible_3d_gizmos);
 +      }
 +
 +      gzmap->update_flag[WM_GIZMOMAP_DRAWSTEP_3D] &= ~GIZMOMAP_IS_REFRESH_CALLBACK;
 +      gzmap->update_flag[WM_GIZMOMAP_DRAWSTEP_2D] &= ~GIZMOMAP_IS_REFRESH_CALLBACK;
 +
 +      return gz;
 +}
 +
 +void WM_gizmomap_add_handlers(ARegion *ar, wmGizmoMap *gzmap)
 +{
 +      wmEventHandler *handler;
 +
 +      for (handler = ar->handlers.first; handler; handler = handler->next) {
 +              if (handler->gizmo_map == gzmap) {
 +                      return;
 +              }
 +      }
 +
 +      handler = MEM_callocN(sizeof(wmEventHandler), "gizmo handler");
 +
 +      BLI_assert(gzmap == ar->gizmo_map);
 +      handler->gizmo_map = gzmap;
 +      BLI_addtail(&ar->handlers, handler);
 +}
 +
 +void wm_gizmomaps_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->gizmo_map) {
 +              return;
 +      }
 +
 +      wmGizmoMap *gzmap = handler->op_region->gizmo_map;
 +      wmGizmo *gz = wm_gizmomap_modal_get(gzmap);
 +      ScrArea *area = CTX_wm_area(C);
 +      ARegion *region = CTX_wm_region(C);
 +
 +      wm_gizmomap_handler_context(C, handler);
 +
 +      /* regular update for running operator */
 +      if (modal_running) {
 +              wmGizmoOpElem *mpop = gz ? WM_gizmo_operator_get(gz, gz->highlight_part) : NULL;
 +              if (gz && mpop && (mpop->type != NULL) && (mpop->type == handler->op->type)) {
 +                      wmGizmoFnModal modal_fn = gz->custom_modal ? gz->custom_modal : gz->type->modal;
 +                      if (modal_fn != NULL) {
 +                              int retval = modal_fn(C, gz, event, 0);
 +                              /* The gizmo 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_gizmomap_highlight_set(gzmap, C, NULL, 0);
 +              if (gz) {
 +                      /* This isn't defined if it ends because of success of cancel, we may want to change. */
 +                      bool cancel = true;
 +                      if (gz->type->exit) {
 +                              gz->type->exit(C, gz, cancel);
 +                      }
 +                      wm_gizmomap_modal_set(gzmap, C, gz, NULL, false);
 +              }
 +      }
 +
 +      /* restore the area */
 +      CTX_wm_area_set(C, area);
 +      CTX_wm_region_set(C, region);
 +}
 +
 +/**
 + * Deselect all selected gizmos in \a gzmap.
 + * \return if selection has changed.
 + */
 +bool wm_gizmomap_deselect_all(wmGizmoMap *gzmap)
 +{
 +      wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
 +
 +      if (msel->items == NULL || msel->len == 0) {
 +              return false;
 +      }
 +
 +      for (int i = 0; i < msel->len; i++) {
 +              wm_gizmo_select_set_ex(gzmap, msel->items[i], false, false, true);
 +      }
 +
 +      wm_gizmomap_select_array_clear(gzmap);
 +
 +      /* always return true, we already checked
 +       * if there's anything to deselect */
 +      return true;
 +}
 +
 +BLI_INLINE bool gizmo_selectable_poll(const wmGizmo *gz, void *UNUSED(data))
 +{
 +      return (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_SELECT);
 +}
 +
 +/**
 + * Select all selectable gizmos in \a gzmap.
 + * \return if selection has changed.
 + */
 +static bool wm_gizmomap_select_all_intern(
 +        bContext *C, wmGizmoMap *gzmap)
 +{
 +      wmGizmoMapSelectState *msel = &gzmap->gzmap_context.select;
 +      /* GHash is used here to avoid having to loop over all gizmos twice (once to
 +       * get tot_sel for allocating, once for actually selecting). Instead we collect
 +       * selectable gizmos in hash table and use this to get tot_sel and do selection */
 +
 +      GHash *hash = WM_gizmomap_gizmo_hash_new(C, gzmap, gizmo_selectable_poll, NULL, true);
 +      GHashIterator gh_iter;
 +      int i;
 +      bool changed = false;
 +
 +      wm_gizmomap_select_array_ensure_len_alloc(gzmap, BLI_ghash_len(hash));
 +
 +      GHASH_ITER_INDEX (gh_iter, hash, i) {
 +              wmGizmo *gz_iter = BLI_ghashIterator_getValue(&gh_iter);
 +              WM_gizmo_select_set(gzmap, gz_iter, true);
 +      }
 +      /* highlight first gizmo */
 +      wm_gizmomap_highlight_set(gzmap, C, msel->items[0], msel->items[0]->highlight_part);
 +
 +      BLI_assert(BLI_ghash_len(hash) == msel->len);
 +
 +      BLI_ghash_free(hash, NULL, NULL);
 +      return changed;
 +}
 +
 +/**
 + * Select/Deselect all selectable gizmos in \a gzmap.
 + * \return if selection has changed.
 + *
 + * TODO select all by type
 + */
 +bool WM_gizmomap_select_all(bContext *C, wmGizmoMap *gzmap, const int action)
 +{
 +      bool changed = false;
 +
 +      switch (action) {
 +              case SEL_SELECT:
 +                      changed = wm_gizmomap_select_all_intern(C, gzmap);
 +                      break;
 +              case SEL_DESELECT:
 +                      changed = wm_gizmomap_deselect_all(gzmap);
 +                      break;
 +              default:
 +                      BLI_assert(0);
 +                      break;
 +      }
 +
 +      if (changed)
 +              WM_event_add_mousemove(C);
 +
 +      return changed;
 +}
 +
 +/**
 + * Prepare context for gizmo handling (but only if area/region is
 + * part of screen). Version of #wm_handler_op_context for gizmos.
 + */
 +void wm_gizmomap_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->gizmo_map == NULL)
 +                                      printf("internal error: modal gizmo-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_gizmomap_cursor_set(const wmGizmoMap *gzmap, wmWindow *win)
 +{
 +      wmGizmo *gz = gzmap->gzmap_context.highlight;
 +      if (gz && gz->type->cursor_get) {
 +              WM_cursor_set(win, gz->type->cursor_get(gz));
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +bool wm_gizmomap_highlight_set(
 +        wmGizmoMap *gzmap, const bContext *C, wmGizmo *gz, int part)
 +{
 +      if ((gz != gzmap->gzmap_context.highlight) ||
 +          (gz && part != gz->highlight_part))
 +      {
 +              if (gzmap->gzmap_context.highlight) {
 +                      gzmap->gzmap_context.highlight->state &= ~WM_GIZMO_STATE_HIGHLIGHT;
 +                      gzmap->gzmap_context.highlight->highlight_part = -1;
 +              }
 +
 +              gzmap->gzmap_context.highlight = gz;
 +
 +              if (gz) {
 +                      gz->state |= WM_GIZMO_STATE_HIGHLIGHT;
 +                      gz->highlight_part = part;
 +                      gzmap->gzmap_context.last_cursor = -1;
 +
 +                      if (C && gz->type->cursor_get) {
 +                              wmWindow *win = CTX_wm_window(C);
 +                              gzmap->gzmap_context.last_cursor = win->cursor;
 +                              WM_cursor_set(win, gz->type->cursor_get(gz));
 +                      }
 +              }
 +              else {
 +                      if (C && gzmap->gzmap_context.last_cursor != -1) {
 +                              wmWindow *win = CTX_wm_window(C);
 +                              WM_cursor_set(win, gzmap->gzmap_context.last_cursor);
 +                      }
 +              }
 +
 +              /* tag the region for redraw */
 +              if (C) {
 +                      ARegion *ar = CTX_wm_region(C);
 +                      ED_region_tag_redraw(ar);
 +              }
 +
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +wmGizmo *wm_gizmomap_highlight_get(wmGizmoMap *gzmap)
 +{
 +      return gzmap->gzmap_context.highlight;
 +}
 +
 +/**
 + * Caller should call exit when (enable == False).
 + */
 +void wm_gizmomap_modal_set(
 +        wmGizmoMap *gzmap, bContext *C, wmGizmo *gz, const wmEvent *event, bool enable)
 +{
 +      if (enable) {
 +              BLI_assert(gzmap->gzmap_context.modal == NULL);
 +              wmWindow *win = CTX_wm_window(C);
 +
 +              WM_tooltip_clear(C, win);
 +
 +              if (gz->type->invoke &&
 +                  (gz->type->modal || gz->custom_modal))
 +              {
 +                      const int retval = gz->type->invoke(C, gz, event);
 +                      if ((retval & OPERATOR_RUNNING_MODAL) == 0) {
 +                              return;
 +                      }
 +              }
 +
 +              gz->state |= WM_GIZMO_STATE_MODAL;
 +              gzmap->gzmap_context.modal = gz;
 +
 +              if ((gz->flag & WM_GIZMO_GRAB_CURSOR) &&
 +                  (event->is_motion_absolute == false))
 +              {
 +                      WM_cursor_grab_enable(win, true, true, NULL);
 +                      copy_v2_v2_int(gzmap->gzmap_context.event_xy, &event->x);
 +                      gzmap->gzmap_context.event_grabcursor = win->grabcursor;
 +              }
 +              else {
 +                      gzmap->gzmap_context.event_xy[0] = INT_MAX;
 +              }
 +
 +              struct wmGizmoOpElem *mpop = WM_gizmo_operator_get(gz, gz->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_gizmomap_modal_set(gzmap, C, gz, event, false);
 +                      }
 +
 +                      /* we failed to hook the gizmo to the operator handler or operator was cancelled, return */
 +                      if (!gzmap->gzmap_context.modal) {
 +                              gz->state &= ~WM_GIZMO_STATE_MODAL;
 +                              MEM_SAFE_FREE(gz->interaction_data);
 +                      }
 +                      return;
 +              }
 +      }
 +      else {
 +              BLI_assert(ELEM(gzmap->gzmap_context.modal, NULL, gz));
 +
 +              /* deactivate, gizmo but first take care of some stuff */
 +              if (gz) {
 +                      gz->state &= ~WM_GIZMO_STATE_MODAL;
 +                      MEM_SAFE_FREE(gz->interaction_data);
 +              }
 +              gzmap->gzmap_context.modal = NULL;
 +
 +              if (C) {
 +                      wmWindow *win = CTX_wm_window(C);
 +                      if (gzmap->gzmap_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 (gzmap->gzmap_context.event_grabcursor == win->grabcursor) {
 +                                      WM_cursor_grab_disable(win, gzmap->gzmap_context.event_xy);
 +                              }
 +                              else {
 +                                      WM_cursor_warp(win, UNPACK2(gzmap->gzmap_context.event_xy));
 +                              }
 +                      }
 +                      ED_region_tag_redraw(CTX_wm_region(C));
 +                      WM_event_add_mousemove(C);
 +              }
 +
 +              gzmap->gzmap_context.event_xy[0] = INT_MAX;
 +      }
 +}
 +
 +wmGizmo *wm_gizmomap_modal_get(wmGizmoMap *gzmap)
 +{
 +      return gzmap->gzmap_context.modal;
 +}
 +
 +wmGizmo **wm_gizmomap_selected_get(wmGizmoMap *gzmap, int *r_selected_len)
 +{
 +      *r_selected_len = gzmap->gzmap_context.select.len;
 +      return gzmap->gzmap_context.select.items;
 +}
 +
 +ListBase *wm_gizmomap_groups_get(wmGizmoMap *gzmap)
 +{
 +      return &gzmap->groups;
 +}
 +
 +void WM_gizmomap_message_subscribe(
 +        bContext *C, wmGizmoMap *gzmap, ARegion *ar, struct wmMsgBus *mbus)
 +{
 +      for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
 +              if (!WM_gizmo_group_type_poll(C, gzgroup->type)) {
 +                      continue;
 +              }
 +              for (wmGizmo *gz = gzgroup->gizmos.first; gz; gz = gz->next) {
 +                      if (gz->flag & WM_GIZMO_HIDDEN) {
 +                              continue;
 +                      }
 +                      WM_gizmo_target_property_subscribe_all(gz, mbus, ar);
 +              }
 +              if (gzgroup->type->message_subscribe != NULL) {
 +                      gzgroup->type->message_subscribe(C, gzgroup, mbus);
 +              }
 +      }
 +}
 +
 +/** \} */ /* wmGizmoMap */
 +
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Tooltip Handling
 + *
 + * \{ */
 +
 +struct ARegion *WM_gizmomap_tooltip_init(
 +        struct bContext *C, struct ARegion *ar, bool *r_exit_on_event)
 +{
 +      wmGizmoMap *gzmap = ar->gizmo_map;
 +      *r_exit_on_event = true;
 +      if (gzmap) {
 +              wmGizmo *gz = gzmap->gzmap_context.highlight;
 +              if (gz) {
 +                      return UI_tooltip_create_from_gizmo(C, gz);
 +              }
 +      }
 +      return NULL;
 +}
 +
 +/** \} */ /* wmGizmoMapType */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name wmGizmoMapType
 + *
 + * \{ */
 +
 +wmGizmoMapType *WM_gizmomaptype_find(
 +        const struct wmGizmoMapType_Params *gzmap_params)
 +{
 +      for (wmGizmoMapType *gzmap_type = gizmomaptypes.first; gzmap_type; gzmap_type = gzmap_type->next) {
 +              if (gzmap_type->spaceid == gzmap_params->spaceid &&
 +                  gzmap_type->regionid == gzmap_params->regionid)
 +              {
 +                      return gzmap_type;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +wmGizmoMapType *WM_gizmomaptype_ensure(
 +        const struct wmGizmoMapType_Params *gzmap_params)
 +{
 +      wmGizmoMapType *gzmap_type = WM_gizmomaptype_find(gzmap_params);
 +
 +      if (gzmap_type) {
 +              return gzmap_type;
 +      }
 +
 +      gzmap_type = MEM_callocN(sizeof(wmGizmoMapType), "gizmotype list");
 +      gzmap_type->spaceid = gzmap_params->spaceid;
 +      gzmap_type->regionid = gzmap_params->regionid;
 +      BLI_addhead(&gizmomaptypes, gzmap_type);
 +
 +      return gzmap_type;
 +}
 +
 +void wm_gizmomaptypes_free(void)
 +{
 +      for (wmGizmoMapType *gzmap_type = gizmomaptypes.first, *gzmap_type_next;
 +           gzmap_type;
 +           gzmap_type = gzmap_type_next)
 +      {
 +              gzmap_type_next = gzmap_type->next;
 +              for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first, *gzgt_next;
 +                   gzgt_ref;
 +                   gzgt_ref = gzgt_next)
 +              {
 +                      gzgt_next = gzgt_ref->next;
 +                      WM_gizmomaptype_group_free(gzgt_ref);
 +              }
 +              MEM_freeN(gzmap_type);
 +      }
 +}
 +
 +/**
 + * Initialize keymaps for all existing gizmo-groups
 + */
 +void wm_gizmos_keymap(wmKeyConfig *keyconf)
 +{
 +      /* we add this item-less keymap once and use it to group gizmo-group keymaps into it */
++      WM_keymap_ensure(keyconf, "Gizmos", 0, 0);
 +
 +      for (wmGizmoMapType *gzmap_type = gizmomaptypes.first; gzmap_type; gzmap_type = gzmap_type->next) {
 +              for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first; gzgt_ref; gzgt_ref = gzgt_ref->next) {
 +                      wm_gizmogrouptype_setup_keymap(gzgt_ref->type, keyconf);
 +              }
 +      }
 +}
 +
 +/** \} */ /* wmGizmoMapType */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Updates for Dynamic Type Registraion
 + *
 + * \{ */
 +
 +
 +void WM_gizmoconfig_update_tag_init(
 +        wmGizmoMapType *gzmap_type, wmGizmoGroupType *gzgt)
 +{
 +      /* tag for update on next use */
 +      gzmap_type->type_update_flag |= (WM_GIZMOMAPTYPE_UPDATE_INIT | WM_GIZMOMAPTYPE_KEYMAP_INIT);
 +      gzgt->type_update_flag |= (WM_GIZMOMAPTYPE_UPDATE_INIT | WM_GIZMOMAPTYPE_KEYMAP_INIT);
 +
 +      wm_gzmap_type_update_flag |= WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT;
 +}
 +
 +void WM_gizmoconfig_update_tag_remove(
 +        wmGizmoMapType *gzmap_type, wmGizmoGroupType *gzgt)
 +{
 +      /* tag for update on next use */
 +      gzmap_type->type_update_flag |= WM_GIZMOMAPTYPE_UPDATE_REMOVE;
 +      gzgt->type_update_flag |= WM_GIZMOMAPTYPE_UPDATE_REMOVE;
 +
 +      wm_gzmap_type_update_flag |= WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE;
 +}
 +
 +/**
 + * Run incase new types have been added (runs often, early exit where possible).
 + * Follows #WM_keyconfig_update concentions.
 + */
 +void WM_gizmoconfig_update(struct Main *bmain)
 +{
 +      if (G.background)
 +              return;
 +
 +      if (wm_gzmap_type_update_flag == 0)
 +              return;
 +
 +      if (wm_gzmap_type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE) {
 +              for (wmGizmoMapType *gzmap_type = gizmomaptypes.first;
 +                   gzmap_type;
 +                   gzmap_type = gzmap_type->next)
 +              {
 +                      if (gzmap_type->type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE) {
 +                              gzmap_type->type_update_flag &= ~WM_GIZMOMAPTYPE_UPDATE_REMOVE;
 +                              for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first, *gzgt_ref_next;
 +                                   gzgt_ref;
 +                                   gzgt_ref = gzgt_ref_next)
 +                              {
 +                                      gzgt_ref_next = gzgt_ref->next;
 +                                      if (gzgt_ref->type->type_update_flag & WM_GIZMOMAPTYPE_UPDATE_REMOVE) {
 +                                              gzgt_ref->type->type_update_flag &= ~WM_GIZMOMAPTYPE_UPDATE_REMOVE;
 +                                              WM_gizmomaptype_group_unlink(NULL, bmain, gzmap_type, gzgt_ref->type);
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              wm_gzmap_type_update_flag &= ~WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE;
 +      }
 +
 +      if (wm_gzmap_type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT) {
 +              for (wmGizmoMapType *gzmap_type = gizmomaptypes.first;
 +                   gzmap_type;
 +                   gzmap_type = gzmap_type->next)
 +              {
 +                      const uchar type_update_all = WM_GIZMOMAPTYPE_UPDATE_INIT | WM_GIZMOMAPTYPE_KEYMAP_INIT;
 +                      if (gzmap_type->type_update_flag & type_update_all) {
 +                              gzmap_type->type_update_flag &= ~type_update_all;
 +                              for (wmGizmoGroupTypeRef *gzgt_ref = gzmap_type->grouptype_refs.first;
 +                                   gzgt_ref;
 +                                   gzgt_ref = gzgt_ref->next)
 +                              {
 +                                      if (gzgt_ref->type->type_update_flag & WM_GIZMOMAPTYPE_KEYMAP_INIT) {
 +                                              WM_gizmomaptype_group_init_runtime_keymap(bmain, gzgt_ref->type);
 +                                              gzgt_ref->type->type_update_flag &= ~WM_GIZMOMAPTYPE_KEYMAP_INIT;
 +                                      }
 +
 +                                      if (gzgt_ref->type->type_update_flag & WM_GIZMOMAPTYPE_UPDATE_INIT) {
 +                                              WM_gizmomaptype_group_init_runtime(bmain, gzmap_type, gzgt_ref->type);
 +                                              gzgt_ref->type->type_update_flag &= ~WM_GIZMOMAPTYPE_UPDATE_INIT;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              wm_gzmap_type_update_flag &= ~WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT;
 +      }
 +}
 +
 +/** \} */
@@@ -764,16 -715,17 +764,16 @@@ void wm_window_ghostwindows_ensure(wmWi
                        wm_window_ghostwindow_add(wm, "Blender", win);
                }
                /* happens after fileread */
 -              if (win->eventstate == NULL)
 -                      win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
 +              wm_window_ensure_eventstate(win);
  
                /* add keymap handlers (1 handler for all keys in map!) */
-               keymap = WM_keymap_find(wm->defaultconf, "Window", 0, 0);
+               keymap = WM_keymap_ensure(wm->defaultconf, "Window", 0, 0);
                WM_event_add_keymap_handler(&win->handlers, keymap);
  
-               keymap = WM_keymap_find(wm->defaultconf, "Screen", 0, 0);
+               keymap = WM_keymap_ensure(wm->defaultconf, "Screen", 0, 0);
                WM_event_add_keymap_handler(&win->handlers, keymap);
  
-               keymap = WM_keymap_find(wm->defaultconf, "Screen Editing", 0, 0);
+               keymap = WM_keymap_ensure(wm->defaultconf, "Screen Editing", 0, 0);
                WM_event_add_keymap_handler(&win->modalhandlers, keymap);
  
                /* add drop boxes */