Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Wed, 14 Nov 2018 00:24:37 +0000 (11:24 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Wed, 14 Nov 2018 00:27:42 +0000 (11:27 +1100)
13 files changed:
1  2 
source/blender/blenkernel/BKE_paint.h
source/blender/blenkernel/intern/paint.c
source/blender/editors/interface/interface_icons.c
source/blender/editors/sculpt_paint/paint_cursor.c
source/blender/editors/sculpt_paint/paint_curve.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_ops.c
source/blender/editors/sculpt_paint/paint_stroke.c
source/blender/editors/sculpt_paint/paint_vertex.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/sculpt_paint/sculpt_uv.c
source/blender/makesrna/intern/rna_brush.c
source/blender/windowmanager/intern/wm_toolsystem.c

@@@ -75,18 -68,18 +75,19 @@@ extern const char PAINT_CURSOR_WEIGHT_P
  extern const char PAINT_CURSOR_TEXTURE_PAINT[3];
  
  typedef enum ePaintMode {
-       ePaintSculpt = 0,
+       PAINT_MODE_SCULPT = 0,
        /** Vertex color. */
-       ePaintVertex = 1,
-       ePaintWeight = 2,
+       PAINT_MODE_VERTEX = 1,
+       PAINT_MODE_WEIGHT = 2,
        /** 3D view (projection painting). */
-       ePaintTexture3D = 3,
+       PAINT_MODE_TEXTURE_3D = 3,
        /** Image space (2D painting). */
-       ePaintTexture2D = 4,
-       ePaintSculptUV = 5,
-       ePaintGpencil = 6,
+       PAINT_MODE_TEXTURE_2D = 4,
+       PAINT_MODE_SCULPT_UV = 5,
++      PAINT_MODE_GPENCIL = 6,
  
-       ePaintInvalid = 7,
+       /** Keep last. */
 -      PAINT_MODE_INVALID = 6
++      PAINT_MODE_INVALID = 7,
  } ePaintMode;
  
  /* overlay invalidation */
@@@ -99,12 -92,13 +100,13 @@@ typedef enum eOverlayControlFlags 
        PAINT_OVERLAY_OVERRIDE_SECONDARY = (1 << 6)
  } eOverlayControlFlags;
  
- #define PAINT_OVERRIDE_MASK (PAINT_OVERLAY_OVERRIDE_SECONDARY | \
-                                                    PAINT_OVERLAY_OVERRIDE_PRIMARY | \
-                                                    PAINT_OVERLAY_OVERRIDE_CURSOR)
+ #define PAINT_OVERRIDE_MASK \
+       (PAINT_OVERLAY_OVERRIDE_SECONDARY | \
+        PAINT_OVERLAY_OVERRIDE_PRIMARY | \
+        PAINT_OVERLAY_OVERRIDE_CURSOR)
  
 -void BKE_paint_invalidate_overlay_tex(struct Scene *scene, const struct Tex *tex);
 -void BKE_paint_invalidate_cursor_overlay(struct Scene *scene, struct CurveMapping *curve);
 +void BKE_paint_invalidate_overlay_tex(struct Scene *scene, struct ViewLayer *view_layer, const struct Tex *tex);
 +void BKE_paint_invalidate_cursor_overlay(struct Scene *scene, struct ViewLayer *view_layer, struct CurveMapping *curve);
  void BKE_paint_invalidate_overlay_all(void);
  eOverlayControlFlags BKE_paint_get_overlay_flags(void);
  void BKE_paint_reset_overlay_invalid(eOverlayControlFlags flag);
@@@ -99,14 -83,14 +99,14 @@@ void BKE_paint_invalidate_overlay_tex(S
                return;
  
        if (br->mtex.tex == tex)
-               overlay_flags |= PAINT_INVALID_OVERLAY_TEXTURE_PRIMARY;
+               overlay_flags |= PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY;
        if (br->mask_mtex.tex == tex)
-               overlay_flags |= PAINT_INVALID_OVERLAY_TEXTURE_SECONDARY;
+               overlay_flags |= PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY;
  }
  
 -void BKE_paint_invalidate_cursor_overlay(Scene *scene, CurveMapping *curve)
 +void BKE_paint_invalidate_cursor_overlay(Scene *scene, ViewLayer *view_layer, CurveMapping *curve)
  {
 -      Paint *p = BKE_paint_get_active(scene);
 +      Paint *p = BKE_paint_get_active(scene, view_layer);
        Brush *br = p->brush;
  
        if (br && br->curve == curve)
@@@ -151,20 -135,18 +151,20 @@@ Paint *BKE_paint_get_active_from_paintm
                ToolSettings *ts = sce->toolsettings;
  
                switch (mode) {
-                       case ePaintSculpt:
+                       case PAINT_MODE_SCULPT:
                                return &ts->sculpt->paint;
-                       case ePaintVertex:
+                       case PAINT_MODE_VERTEX:
                                return &ts->vpaint->paint;
-                       case ePaintWeight:
+                       case PAINT_MODE_WEIGHT:
                                return &ts->wpaint->paint;
-                       case ePaintTexture2D:
-                       case ePaintTexture3D:
+                       case PAINT_MODE_TEXTURE_2D:
+                       case PAINT_MODE_TEXTURE_3D:
                                return &ts->imapaint.paint;
-                       case ePaintSculptUV:
+                       case PAINT_MODE_SCULPT_UV:
                                return &ts->uvsculpt->paint;
-                       case ePaintGpencil:
++                      case PAINT_MODE_GPENCIL:
 +                              return &ts->gp_paint->paint;
-                       case ePaintInvalid:
+                       case PAINT_MODE_INVALID:
                                return NULL;
                        default:
                                return &ts->imapaint.paint;
        return NULL;
  }
  
 -Paint *BKE_paint_get_active(Scene *sce)
 +const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode)
  {
 -      if (sce) {
 +      switch (mode) {
-               case ePaintSculpt:
++              case PAINT_MODE_SCULPT:
 +                      return rna_enum_brush_sculpt_tool_items;
-               case ePaintVertex:
++              case PAINT_MODE_VERTEX:
 +                      return rna_enum_brush_vertex_tool_items;
-               case ePaintWeight:
++              case PAINT_MODE_WEIGHT:
 +                      return rna_enum_brush_weight_tool_items;
-               case ePaintTexture2D:
-               case ePaintTexture3D:
++              case PAINT_MODE_TEXTURE_2D:
++              case PAINT_MODE_TEXTURE_3D:
 +                      return rna_enum_brush_image_tool_items;
-               case ePaintSculptUV:
++              case PAINT_MODE_SCULPT_UV:
 +                      return NULL;
-               case ePaintGpencil:
++              case PAINT_MODE_GPENCIL:
 +                      return rna_enum_brush_gpencil_types_items;
-               case ePaintInvalid:
++              case PAINT_MODE_INVALID:
 +                      break;
 +      }
 +      return NULL;
 +}
 +
 +const char *BKE_paint_get_tool_prop_id_from_paintmode(ePaintMode mode)
 +{
 +      switch (mode) {
-               case ePaintSculpt: return "sculpt_tool";
-               case ePaintVertex: return "vertex_tool";
-               case ePaintWeight: return "weight_tool";
-               case ePaintTexture2D:
-               case ePaintTexture3D: return "image_tool";
-               case ePaintGpencil: return "gpencil_tool";
++              case PAINT_MODE_SCULPT: return "sculpt_tool";
++              case PAINT_MODE_VERTEX: return "vertex_tool";
++              case PAINT_MODE_WEIGHT: return "weight_tool";
++              case PAINT_MODE_TEXTURE_2D:
++              case PAINT_MODE_TEXTURE_3D: return "image_tool";
++              case PAINT_MODE_GPENCIL: return "gpencil_tool";
 +              default:
 +                      /* invalid paint mode */
 +                      return NULL;
 +      }
 +}
 +
 +Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
 +{
 +      if (sce && view_layer) {
                ToolSettings *ts = sce->toolsettings;
  
 -              if (sce->basact && sce->basact->object) {
 -                      switch (sce->basact->object->mode) {
 +              if (view_layer->basact && view_layer->basact->object) {
 +                      switch (view_layer->basact->object->mode) {
                                case OB_MODE_SCULPT:
                                        return &ts->sculpt->paint;
                                case OB_MODE_VERTEX_PAINT:
@@@ -324,35 -263,9 +324,35 @@@ ePaintMode BKE_paintmode_get_active_fro
                }
        }
  
-       return ePaintInvalid;
+       return PAINT_MODE_INVALID;
  }
  
-                               return ePaintSculpt;
 +ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref)
 +{
 +      if (tref->space_type == SPACE_VIEW3D) {
 +              switch (tref->mode) {
 +                      case CTX_MODE_SCULPT:
-                               return ePaintVertex;
++                              return PAINT_MODE_SCULPT;
 +                      case CTX_MODE_PAINT_VERTEX:
-                               return ePaintWeight;
++                              return PAINT_MODE_VERTEX;
 +                      case CTX_MODE_PAINT_WEIGHT:
-                               return ePaintGpencil;
++                              return PAINT_MODE_WEIGHT;
 +                      case CTX_MODE_GPENCIL_PAINT:
-                               return ePaintTexture3D;
++                              return PAINT_MODE_GPENCIL;
 +                      case CTX_MODE_PAINT_TEXTURE:
-                               return ePaintTexture2D;
++                              return PAINT_MODE_TEXTURE_3D;
 +              }
 +      }
 +      else if (tref->space_type == SPACE_IMAGE) {
 +              switch (tref->mode) {
 +                      case SI_MODE_PAINT:
-       return ePaintInvalid;
++                              return PAINT_MODE_TEXTURE_2D;
 +              }
 +      }
 +
++      return PAINT_MODE_INVALID;
 +}
 +
  Brush *BKE_paint_brush(Paint *p)
  {
        return p ? p->brush : NULL;
@@@ -401,27 -280,6 +401,27 @@@ void BKE_paint_runtime_init(const ToolS
        }
  }
  
-               case ePaintTexture2D:
-               case ePaintTexture3D:
 +uint BKE_paint_get_brush_tool_offset_from_paintmode(const ePaintMode mode)
 +{
 +      switch (mode) {
-               case ePaintSculpt:
++              case PAINT_MODE_TEXTURE_2D:
++              case PAINT_MODE_TEXTURE_3D:
 +                      return offsetof(Brush, imagepaint_tool);
-               case ePaintVertex:
++              case PAINT_MODE_SCULPT:
 +                      return offsetof(Brush, sculpt_tool);
-               case ePaintWeight:
++              case PAINT_MODE_VERTEX:
 +                      return offsetof(Brush, vertexpaint_tool);
-               case ePaintGpencil:
++              case PAINT_MODE_WEIGHT:
 +                      return offsetof(Brush, weightpaint_tool);
-               case ePaintSculptUV:
-               case ePaintInvalid:
++              case PAINT_MODE_GPENCIL:
 +                      return offsetof(Brush, gpencil_tool);
++              case PAINT_MODE_SCULPT_UV:
++              case PAINT_MODE_INVALID:
 +                      break; /* We don't use these yet. */
 +      }
 +      return 0;
 +}
 +
  /** Free (or release) any data used by this paint curve (does not free the pcurve itself). */
  void BKE_paint_curve_free(PaintCurve *pc)
  {
@@@ -617,21 -477,22 +617,21 @@@ void BKE_paint_cavity_curve_preset(Pain
        curvemapping_changed(p->cavity_curve, false);
  }
  
 -eObjectMode BKE_paint_object_mode_from_paint_mode(ePaintMode mode)
 +eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode)
  {
        switch (mode) {
-               case ePaintSculpt:
+               case PAINT_MODE_SCULPT:
                        return OB_MODE_SCULPT;
-               case ePaintVertex:
+               case PAINT_MODE_VERTEX:
                        return OB_MODE_VERTEX_PAINT;
-               case ePaintWeight:
+               case PAINT_MODE_WEIGHT:
                        return OB_MODE_WEIGHT_PAINT;
-               case ePaintTexture2D:
-               case ePaintTexture3D:
 -              case PAINT_MODE_TEXTURE_3D:
 -                      return OB_MODE_TEXTURE_PAINT;
+               case PAINT_MODE_TEXTURE_2D:
++              case PAINT_MODE_TEXTURE_3D:
                        return OB_MODE_TEXTURE_PAINT;
-               case ePaintSculptUV:
+               case PAINT_MODE_SCULPT_UV:
                        return OB_MODE_EDIT;
-               case ePaintInvalid:
+               case PAINT_MODE_INVALID:
                default:
                        return 0;
        }
@@@ -1670,106 -1187,48 +1670,106 @@@ static int ui_id_brush_get_icon(const b
  
        if (br->flag & BRUSH_CUSTOM_ICON) {
                BKE_icon_id_ensure(id);
 -              ui_id_brush_render(C, id);
 +              ui_id_icon_render(C, id, true);
        }
        else {
 +              WorkSpace *workspace = CTX_wm_workspace(C);
                Object *ob = CTX_data_active_object(C);
 -              SpaceImage *sima;
                const EnumPropertyItem *items = NULL;
-               ePaintMode paint_mode = ePaintInvalid;
 -              int tool = PAINT_TOOL_DRAW, mode = 0;
++              ePaintMode paint_mode = PAINT_MODE_INVALID;
 +              ScrArea *sa = CTX_wm_area(C);
 +              char space_type = sa->spacetype;
 +              /* When in an unsupported space. */
 +              if (!ELEM(space_type, SPACE_VIEW3D, SPACE_IMAGE)) {
 +                      space_type = workspace->tools_space_type;
 +              }
  
                /* XXX: this is not nice, should probably make brushes
                 * be strictly in one paint mode only to avoid
                 * checking various context stuff here */
  
 -              if (CTX_wm_view3d(C) && ob) {
 -                      if (ob->mode & OB_MODE_SCULPT)
 -                              mode = OB_MODE_SCULPT;
 -                      else if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT))
 -                              mode = OB_MODE_VERTEX_PAINT;
 -                      else if (ob->mode & OB_MODE_TEXTURE_PAINT)
 -                              mode = OB_MODE_TEXTURE_PAINT;
 +              if ((space_type == SPACE_VIEW3D) && ob) {
 +                      if (ob->mode & OB_MODE_SCULPT) {
-                               paint_mode = ePaintSculpt;
++                              paint_mode = PAINT_MODE_SCULPT;
 +                      }
 +                      else if (ob->mode & OB_MODE_VERTEX_PAINT) {
-                               paint_mode = ePaintVertex;
++                              paint_mode = PAINT_MODE_VERTEX;
 +                      }
 +                      else if (ob->mode & OB_MODE_WEIGHT_PAINT) {
-                               paint_mode = ePaintWeight;
++                              paint_mode = PAINT_MODE_WEIGHT;
 +                      }
 +                      else if (ob->mode & OB_MODE_TEXTURE_PAINT) {
-                               paint_mode = ePaintTexture3D;
++                              paint_mode = PAINT_MODE_TEXTURE_3D;
 +                      }
                }
 -              else if ((sima = CTX_wm_space_image(C)) &&
 -                       (sima->mode == SI_MODE_PAINT))
 -              {
 -                      mode = OB_MODE_TEXTURE_PAINT;
 +              else if (space_type == SPACE_IMAGE) {
 +                      int sima_mode;
 +                      if (sa->spacetype == space_type) {
 +                              SpaceImage *sima = sa->spacedata.first;
 +                              sima_mode = sima->mode;
 +                      }
 +                      else {
 +                              sima_mode = workspace->tools_mode;
 +                      }
 +
 +                      if (sima_mode == SI_MODE_PAINT) {
-                               paint_mode = ePaintTexture2D;
++                              paint_mode = PAINT_MODE_TEXTURE_2D;
 +                      }
                }
  
                /* reset the icon */
 -              if (mode == OB_MODE_SCULPT) {
 -                      items = rna_enum_brush_sculpt_tool_items;
 -                      tool = br->sculpt_tool;
 -              }
 -              else if (mode == OB_MODE_VERTEX_PAINT) {
 -                      items = rna_enum_brush_vertex_tool_items;
 -                      tool = br->vertexpaint_tool;
 +              if ((ob != NULL) &&
 +                  (ob->mode & OB_MODE_GPENCIL_PAINT) &&
 +                  (br->gpencil_settings != NULL))
 +              {
 +                      switch (br->gpencil_settings->icon_id) {
 +                              case GP_BRUSH_ICON_PENCIL:
 +                                      br->id.icon_id = ICON_GPBRUSH_PENCIL;
 +                                      break;
 +                              case GP_BRUSH_ICON_PEN:
 +                                      br->id.icon_id = ICON_GPBRUSH_PEN;
 +                                      break;
 +                              case GP_BRUSH_ICON_INK:
 +                                      br->id.icon_id = ICON_GPBRUSH_INK;
 +                                      break;
 +                              case GP_BRUSH_ICON_INKNOISE:
 +                                      br->id.icon_id = ICON_GPBRUSH_INKNOISE;
 +                                      break;
 +                              case GP_BRUSH_ICON_BLOCK:
 +                                      br->id.icon_id = ICON_GPBRUSH_BLOCK;
 +                                      break;
 +                              case GP_BRUSH_ICON_MARKER:
 +                                      br->id.icon_id = ICON_GPBRUSH_MARKER;
 +                                      break;
 +                              case GP_BRUSH_ICON_FILL:
 +                                      br->id.icon_id = ICON_GPBRUSH_FILL;
 +                                      break;
 +                              case GP_BRUSH_ICON_ERASE_SOFT:
 +                                      br->id.icon_id = ICON_GPBRUSH_ERASE_SOFT;
 +                                      break;
 +                              case GP_BRUSH_ICON_ERASE_HARD:
 +                                      br->id.icon_id = ICON_GPBRUSH_ERASE_HARD;
 +                                      break;
 +                              case GP_BRUSH_ICON_ERASE_STROKE:
 +                                      br->id.icon_id = ICON_GPBRUSH_ERASE_STROKE;
 +                                      break;
 +                              default:
 +                                      br->id.icon_id = ICON_GPBRUSH_PEN;
 +                                      break;
 +                      }
 +                      return id->icon_id;
                }
-               else if (paint_mode != ePaintInvalid) {
 -              else if (mode == OB_MODE_TEXTURE_PAINT) {
 -                      items = rna_enum_brush_image_tool_items;
 -                      tool = br->imagepaint_tool;
++              else if (paint_mode != PAINT_MODE_INVALID) {
 +                      items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
 +                      const uint tool_offset = BKE_paint_get_brush_tool_offset_from_paintmode(paint_mode);
 +                      const int tool_type = *(char *)POINTER_OFFSET(br, tool_offset);
 +                      if (!items || !RNA_enum_icon_from_value(items, tool_type, &id->icon_id)) {
 +                              id->icon_id = 0;
 +                      }
                }
 -
 -              if (!items || !RNA_enum_icon_from_value(items, tool, &id->icon_id))
 +              else {
                        id->icon_id = 0;
 +              }
        }
  
        return id->icon_id;
@@@ -802,15 -788,21 +802,15 @@@ static void paint_draw_alpha_overlay
          ViewContext *vc, int x, int y, float zoom, ePaintMode mode)
  {
        /* color means that primary brush texture is colured and secondary is used for alpha/mask control */
-       bool col = ELEM(mode, ePaintTexture3D, ePaintTexture2D, ePaintVertex) ? true : false;
+       bool col = ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX) ? true : false;
        eOverlayControlFlags flags = BKE_paint_get_overlay_flags();
 -      /* save lots of GL state
 -       * TODO: check on whether all of these are needed? */
 -      glPushAttrib(GL_COLOR_BUFFER_BIT |
 -                   GL_CURRENT_BIT |
 -                   GL_DEPTH_BUFFER_BIT |
 -                   GL_ENABLE_BIT |
 -                   GL_LINE_BIT |
 -                   GL_POLYGON_BIT |
 -                   GL_STENCIL_BUFFER_BIT |
 -                   GL_TRANSFORM_BIT |
 -                   GL_VIEWPORT_BIT |
 -                   GL_TEXTURE_BIT);
 +      gpuPushAttrib(GPU_DEPTH_BUFFER_BIT | GPU_BLEND_BIT);
  
 +      /* Translate to region. */
 +      GPU_matrix_push();
 +      GPU_matrix_translate_2f(vc->ar->winrct.xmin, vc->ar->winrct.ymin);
 +      x -= vc->ar->winrct.xmin;
 +      y -= vc->ar->winrct.ymin;
  
        /* coloured overlay should be drawn separately */
        if (col) {
@@@ -1090,12 -1050,13 +1090,12 @@@ static void paint_draw_cursor(bContext 
  
        /* TODO: as sculpt and other paint modes are unified, this
         * special mode of drawing will go away */
-       if ((mode == ePaintSculpt) && vc.obact->sculpt) {
+       if ((mode == PAINT_MODE_SCULPT) && vc.obact->sculpt) {
                float location[3];
                int pixel_radius;
 -              bool hit;
  
                /* test if brush is over the mesh */
 -              hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location, ups);
 +              bool hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location, ups);
  
                if (BKE_brush_use_locked_size(scene, brush))
                        BKE_brush_size_set(scene, brush, pixel_radius);
@@@ -757,20 -720,16 +757,20 @@@ void ED_space_image_paint_update(Main *
        ImagePaintSettings *imapaint = &settings->imapaint;
        bool enabled = false;
  
 -      for (win = wm->windows.first; win; win = win->next)
 -              for (sa = win->screen->areabase.first; sa; sa = sa->next)
 -                      if (sa->spacetype == SPACE_IMAGE)
 -                              if (((SpaceImage *)sa->spacedata.first)->mode == SI_MODE_PAINT)
 +      for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +              bScreen *screen = WM_window_get_active_screen(win);
 +
 +              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                      if (sa->spacetype == SPACE_IMAGE) {
 +                              if (((SpaceImage *)sa->spacedata.first)->mode == SI_MODE_PAINT) {
                                        enabled = true;
 +                              }
 +                      }
 +              }
 +      }
  
        if (enabled) {
-               BKE_paint_init(bmain, scene, ePaintTexture2D, PAINT_CURSOR_TEXTURE_PAINT);
+               BKE_paint_init(bmain, scene, PAINT_MODE_TEXTURE_2D, PAINT_CURSOR_TEXTURE_PAINT);
  
                paint_cursor_start_explicit(&imapaint->paint, wm, image_paint_poll);
        }
@@@ -1130,10 -1093,8 +1130,10 @@@ static int texture_paint_toggle_exec(bC
  
                ob->mode |= mode_flag;
  
-               BKE_paint_init(bmain, scene, ePaintTexture3D, PAINT_CURSOR_TEXTURE_PAINT);
+               BKE_paint_init(bmain, scene, PAINT_MODE_TEXTURE_3D, PAINT_CURSOR_TEXTURE_PAINT);
  
 +              BKE_paint_toolslots_brush_validate(bmain, &imapaint->paint);
 +
                if (U.glreslimit != 0)
                        GPU_free_images(bmain);
                GPU_paint_set_mipmap(bmain, 0);
@@@ -106,43 -95,6 +106,43 @@@ static void BRUSH_OT_add(wmOperatorTyp
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
  }
  
-       // ePaintMode mode = ePaintGpencil;
 +static int brush_add_gpencil_exec(bContext *C, wmOperator *UNUSED(op))
 +{
 +      /*int type = RNA_enum_get(op->ptr, "type");*/
 +      ToolSettings *ts = CTX_data_tool_settings(C);
 +      Paint *paint = &ts->gp_paint->paint;
 +      Brush *br = BKE_paint_brush(paint);
 +      Main *bmain = CTX_data_main(C);
++      // ePaintMode mode = PAINT_MODE_GPENCIL;
 +
 +      if (br) {
 +              br = BKE_brush_copy(bmain, br);
 +      }
 +      else {
 +              br = BKE_brush_add(bmain, "Brush", OB_MODE_GPENCIL_PAINT);
 +              id_us_min(&br->id);  /* fake user only */
 +      }
 +
 +      BKE_paint_brush_set(paint, br);
 +
 +      /* TODO init grease pencil specific data */
 +
 +      return OPERATOR_FINISHED;
 +}
 +
 +static void BRUSH_OT_add_gpencil(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Add Drawing Brush";
 +      ot->description = "Add brush for Grease Pencil";
 +      ot->idname = "BRUSH_OT_add_gpencil";
 +
 +      /* api callbacks */
 +      ot->exec = brush_add_gpencil_exec;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 +}
  
  static int brush_scale_size_exec(bContext *C, wmOperator *op)
  {
@@@ -457,10 -409,15 +457,10 @@@ static int brush_select_exec(bContext *
        const bool create_missing = RNA_boolean_get(op->ptr, "create_missing");
        const bool toggle = RNA_boolean_get(op->ptr, "toggle");
        const char *tool_name = "Brush";
 -      size_t tool_offset;
  
-       if (paint_mode == ePaintInvalid) {
 -      if (paint_mode == OB_MODE_ACTIVE) {
 -              Object *ob = CTX_data_active_object(C);
 -              if (ob) {
 -                      /* select current paint mode */
 -                      paint_mode = ob->mode & OB_MODE_ALL_PAINT;
 -              }
 -              else {
++      if (paint_mode == PAINT_MODE_INVALID) {
 +              paint_mode = BKE_paintmode_get_active_from_context(C);
-               if (paint_mode == ePaintInvalid) {
++              if (paint_mode == PAINT_MODE_INVALID) {
                        return OPERATOR_CANCELLED;
                }
        }
  
  static void PAINT_OT_brush_select(wmOperatorType *ot)
  {
 +      /* Keep names matching 'rna_enum_object_mode_items' (besides active). */
        static const EnumPropertyItem paint_mode_items[] = {
-               {ePaintInvalid, "ACTIVE", 0, "Current", "Set brush for active paint mode"},
-               {ePaintSculpt, "SCULPT", ICON_SCULPTMODE_HLT, "Sculpt", ""},
-               {ePaintVertex, "VERTEX_PAINT", ICON_VPAINT_HLT, "Vertex Paint", ""},
-               {ePaintWeight, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""},
-               {ePaintTexture3D, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""},
-               {ePaintGpencil, "GPENCIL_PAINT", ICON_GREASEPENCIL, "Grease Pencil Paint", ""},
 -              {OB_MODE_ACTIVE, "ACTIVE", 0, "Current", "Set brush for active paint mode"},
 -              {OB_MODE_SCULPT, "SCULPT", ICON_SCULPTMODE_HLT, "Sculpt", ""},
 -              {OB_MODE_VERTEX_PAINT, "VERTEX_PAINT", ICON_VPAINT_HLT, "Vertex Paint", ""},
 -              {OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""},
 -              {OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""},
++              {PAINT_MODE_INVALID, "ACTIVE", 0, "Current", "Set brush for active paint mode"},
++              {PAINT_MODE_SCULPT, "SCULPT", ICON_SCULPTMODE_HLT, "Sculpt", ""},
++              {PAINT_MODE_VERTEX, "VERTEX_PAINT", ICON_VPAINT_HLT, "Vertex Paint", ""},
++              {PAINT_MODE_WEIGHT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""},
++              {PAINT_MODE_TEXTURE_3D, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""},
++              {PAINT_MODE_GPENCIL, "GPENCIL_PAINT", ICON_GREASEPENCIL, "Grease Pencil Paint", ""},
                {0, NULL, 0, NULL, NULL}
        };
        PropertyRNA *prop;
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
  
        /* props */
 -      RNA_def_enum(ot->srna, "paint_mode", paint_mode_items, OB_MODE_ACTIVE, "Paint Mode", "");
 -      RNA_def_enum(ot->srna, "sculpt_tool", rna_enum_brush_sculpt_tool_items, 0, "Sculpt Tool", "");
 -      RNA_def_enum(ot->srna, "vertex_paint_tool", rna_enum_brush_vertex_tool_items, 0, "Vertex Paint Tool", "");
 -      RNA_def_enum(ot->srna, "weight_paint_tool", rna_enum_brush_vertex_tool_items, 0, "Weight Paint Tool", "");
 -      RNA_def_enum(ot->srna, "texture_paint_tool", rna_enum_brush_image_tool_items, 0, "Texture Paint Tool", "");
 +      /* All properties are hidden, so as not to show the redo panel. */
-       prop = RNA_def_enum(ot->srna, "paint_mode", paint_mode_items, ePaintInvalid, "Paint Mode", "");
++      prop = RNA_def_enum(ot->srna, "paint_mode", paint_mode_items, PAINT_MODE_INVALID, "Paint Mode", "");
 +      RNA_def_property_flag(prop, PROP_HIDDEN);
 +
 +      for (const EnumPropertyItem *item = paint_mode_items + 1; item->identifier; item++) {
 +              const ePaintMode paint_mode = item->value;
 +              const char *prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
 +              prop = RNA_def_enum(ot->srna, prop_id, BKE_paint_get_tool_enum_from_paintmode(paint_mode), 0, prop_id, "");
 +              RNA_def_property_flag(prop, PROP_HIDDEN);
 +      }
  
        prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "Toggle between two brushes rather than cycling");
 -      RNA_def_property_flag(prop, PROP_SKIP_SAVE);
 +      RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
        prop = RNA_def_boolean(ot->srna, "create_missing", 0, "Create Missing", "If the requested brush type does not exist, create a new brush");
 -      RNA_def_property_flag(prop, PROP_SKIP_SAVE);
 -}
 -
 -static wmKeyMapItem *keymap_brush_select(
 -        wmKeyMap *keymap, int paint_mode,
 -        int tool, int keymap_type,
 -        int keymap_modifier)
 -{
 -      wmKeyMapItem *kmi;
 -      kmi = WM_keymap_add_item(
 -              keymap, "PAINT_OT_brush_select",
 -              keymap_type, KM_PRESS, keymap_modifier, 0);
 -
 -      RNA_enum_set(kmi->ptr, "paint_mode", paint_mode);
 -
 -      switch (paint_mode) {
 -              case OB_MODE_SCULPT:
 -                      RNA_enum_set(kmi->ptr, "sculpt_tool", tool);
 -                      break;
 -              case OB_MODE_VERTEX_PAINT:
 -                      RNA_enum_set(kmi->ptr, "vertex_paint_tool", tool);
 -                      break;
 -              case OB_MODE_WEIGHT_PAINT:
 -                      RNA_enum_set(kmi->ptr, "weight_paint_tool", tool);
 -                      break;
 -              case OB_MODE_TEXTURE_PAINT:
 -                      RNA_enum_set(kmi->ptr, "texture_paint_tool", tool);
 -                      break;
 -      }
 -
 -      return kmi;
 +      RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
  }
  
  static int brush_uv_sculpt_tool_set_exec(bContext *C, wmOperator *op)
@@@ -1073,24 -1045,25 +1073,24 @@@ static void ed_vwpaintmode_enter_generi
        ob->mode |= mode_flag;
        Mesh *me = BKE_mesh_from_object(ob);
  
 +      /* Same as sculpt mode, make sure we don't have cached derived mesh which
 +       * points to freed arrays.
 +       */
 +      BKE_object_free_derived_caches(ob);
 +
        if (mode_flag == OB_MODE_VERTEX_PAINT) {
-               const ePaintMode paint_mode = ePaintVertex;
+               const ePaintMode paint_mode = PAINT_MODE_VERTEX;
                ED_mesh_color_ensure(me, NULL);
  
 -              if (scene->toolsettings->vpaint == NULL) {
 -                      scene->toolsettings->vpaint = new_vpaint();
 -              }
 -
 +              BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->vpaint);
                Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
                paint_cursor_start_explicit(paint, wm, vertex_paint_poll);
                BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_VERTEX_PAINT);
        }
        else if (mode_flag == OB_MODE_WEIGHT_PAINT) {
-               const  ePaintMode paint_mode = ePaintWeight;
+               const  ePaintMode paint_mode = PAINT_MODE_WEIGHT;
  
 -              if (scene->toolsettings->wpaint == NULL) {
 -                      scene->toolsettings->wpaint = new_vpaint();
 -              }
 -
 +              BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->wpaint);
                Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
                paint_cursor_start_explicit(paint, wm, weight_paint_poll);
                BKE_paint_init(bmain, scene, paint_mode, PAINT_CURSOR_WEIGHT_PAINT);
@@@ -240,14 -240,14 +240,14 @@@ void ED_space_image_uv_sculpt_update(Ma
                        settings->uv_sculpt_tool = UV_SCULPT_TOOL_GRAB;
                        settings->uv_sculpt_settings = UV_SCULPT_LOCK_BORDERS | UV_SCULPT_ALL_ISLANDS;
                        settings->uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN;
 -                      /* Uv sculpting does not include explicit brush view control yet, always enable */
 -                      settings->uvsculpt->paint.flags |= PAINT_SHOW_BRUSH;
                }
 -
 +              BKE_paint_ensure(settings, (Paint **)&settings->uvsculpt);
-               BKE_paint_init(bmain, scene, ePaintSculptUV, PAINT_CURSOR_SCULPT);
+               BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT_UV, PAINT_CURSOR_SCULPT);
  
                settings->uvsculpt->paint.paint_cursor = WM_paint_cursor_activate(
 -                      wm, uv_sculpt_brush_poll,
 +                      wm,
 +                      SPACE_IMAGE, RGN_TYPE_WINDOW,
 +                      uv_sculpt_brush_poll,
                        brush_drawcursor_uvsculpt, NULL);
        }
        else {
index b1b5b54,0000000..7b91f83
mode 100644,000000..100644
--- /dev/null
@@@ -1,923 -1,0 +1,923 @@@
-                       BLI_assert(paint_mode != ePaintInvalid);
 +/*
 + * ***** BEGIN GPL LICENSE BLOCK *****
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/windowmanager/intern/wm_toolsystem.c
 + *  \ingroup wm
 + *
 + * Experimental tool-system>
 + */
 +
 +#include <string.h>
 +
 +#include "CLG_log.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_utildefines.h"
 +#include "BLI_string.h"
 +#include "BLI_listbase.h"
 +
 +#include "DNA_ID.h"
 +#include "DNA_scene_types.h"
 +#include "DNA_space_types.h"
 +#include "DNA_windowmanager_types.h"
 +#include "DNA_workspace_types.h"
 +#include "DNA_object_types.h"
 +
 +#include "BKE_brush.h"
 +#include "BKE_context.h"
 +#include "BKE_idprop.h"
 +#include "BKE_library.h"
 +#include "BKE_main.h"
 +#include "BKE_paint.h"
 +#include "BKE_workspace.h"
 +
 +#include "RNA_access.h"
 +#include "RNA_enum_types.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +#include "WM_message.h"
 +#include "WM_toolsystem.h"  /* own include */
 +
 +static void toolsystem_reinit_with_toolref(
 +        bContext *C, WorkSpace *UNUSED(workspace), bToolRef *tref);
 +static bToolRef *toolsystem_reinit_ensure_toolref(
 +        bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *default_tool);
 +static void toolsystem_refresh_screen_from_active_tool(
 +        Main *bmain, WorkSpace *workspace, bToolRef *tref);
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Tool Reference API
 + * \{ */
 +
 +struct bToolRef *WM_toolsystem_ref_from_context(struct bContext *C)
 +{
 +      WorkSpace *workspace = CTX_wm_workspace(C);
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      ScrArea *sa = CTX_wm_area(C);
 +      if (((1 << sa->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) == 0) {
 +              return NULL;
 +      }
 +      const bToolKey tkey = {
 +              .space_type = sa->spacetype,
 +              .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
 +      };
 +      bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
 +      /* We could return 'sa->runtime.tool' in this case. */
 +      if (sa->runtime.is_tool_set) {
 +              BLI_assert(tref == sa->runtime.tool);
 +      }
 +      return tref;
 +}
 +
 +struct bToolRef_Runtime *WM_toolsystem_runtime_from_context(struct bContext *C)
 +{
 +      bToolRef *tref = WM_toolsystem_ref_from_context(C);
 +      return tref ? tref->runtime : NULL;
 +}
 +
 +bToolRef *WM_toolsystem_ref_find(WorkSpace *workspace, const bToolKey *tkey)
 +{
 +      BLI_assert((1 << tkey->space_type) & WM_TOOLSYSTEM_SPACE_MASK);
 +      LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
 +              if ((tref->space_type == tkey->space_type) &&
 +                  (tref->mode == tkey->mode))
 +              {
 +                      return tref;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +bToolRef_Runtime *WM_toolsystem_runtime_find(WorkSpace *workspace, const bToolKey *tkey)
 +{
 +      bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
 +      return tref ? tref->runtime : NULL;
 +}
 +
 +bool WM_toolsystem_ref_ensure(
 +        struct WorkSpace *workspace, const bToolKey *tkey,
 +        bToolRef **r_tref)
 +{
 +      bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
 +      if (tref) {
 +              *r_tref = tref;
 +              return false;
 +      }
 +      tref = MEM_callocN(sizeof(*tref), __func__);
 +      BLI_addhead(&workspace->tools, tref);
 +      tref->space_type = tkey->space_type;
 +      tref->mode = tkey->mode;
 +      *r_tref = tref;
 +      return true;
 +}
 +
 +/** \} */
 +
 +
 +static void toolsystem_unlink_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
 +{
 +      bToolRef_Runtime *tref_rt = tref->runtime;
 +
 +      if (tref_rt->gizmo_group[0]) {
 +              wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(tref_rt->gizmo_group, false);
 +              if (gzgt != NULL) {
 +                      bool found = false;
 +
 +                      /* TODO(campbell) */
 +                      Main *bmain = CTX_data_main(C);
 +#if 0
 +                      wmWindowManager *wm = bmain->wm.first;
 +                      /* Check another workspace isn't using this tool. */
 +                      for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                              const WorkSpace *workspace_iter = WM_window_get_active_workspace(win);
 +                              if (workspace != workspace_iter) {
 +                                      if (STREQ(workspace->tool.gizmo_group, workspace_iter->tool.gizmo_group)) {
 +                                              found = true;
 +                                              break;
 +                                      }
 +                              }
 +                      }
 +#else
 +                      UNUSED_VARS(workspace);
 +#endif
 +                      if (!found) {
 +                              wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
 +                              WM_gizmomaptype_group_unlink(C, bmain, gzmap_type, gzgt);
 +                      }
 +              }
 +      }
 +}
 +void WM_toolsystem_unlink(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
 +{
 +      bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
 +      if (tref && tref->runtime) {
 +              toolsystem_unlink_ref(C, workspace, tref);
 +      }
 +}
 +
 +static void toolsystem_ref_link__refresh_image_uv_sculpt(bContext *C, Scene *scene)
 +{
 +      PointerRNA ptr;
 +      RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &ptr);
 +      PropertyRNA *prop = RNA_struct_find_property(&ptr, "use_uv_sculpt");
 +      RNA_property_update(C, &ptr, prop);
 +}
 +
 +/**
 + * \see #toolsystem_ref_link
 + */
 +static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tref)
 +{
 +      bToolRef_Runtime *tref_rt = tref->runtime;
 +      if (tref_rt->gizmo_group[0]) {
 +              const char *idname = tref_rt->gizmo_group;
 +              wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
 +              if (gzgt != NULL) {
 +                      if ((gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_INIT) == 0) {
 +                              WM_gizmo_group_type_ensure_ptr(gzgt);
 +                      }
 +              }
 +              else {
 +                      CLOG_WARN(WM_LOG_TOOLS, "'%s' widget not found", idname);
 +              }
 +      }
 +
 +      if (tref_rt->data_block[0]) {
 +              Main *bmain = CTX_data_main(C);
 +
 +              if ((tref->space_type == SPACE_VIEW3D) &&
 +                  (tref->mode == CTX_MODE_GPENCIL_SCULPT))
 +              {
 +                      const EnumPropertyItem *items = rna_enum_gpencil_sculpt_brush_items;
 +                      const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
 +                      if (i != -1) {
 +                              const int value = items[i].value;
 +                              wmWindowManager *wm = bmain->wm.first;
 +                              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                                      if (workspace == WM_window_get_active_workspace(win)) {
 +                                              Scene *scene = WM_window_get_active_scene(win);
 +                                              ToolSettings *ts = scene->toolsettings;
 +                                              ts->gp_sculpt.brushtype = value;
 +                                      }
 +                              }
 +                      }
 +              }
 +              else if ((tref->space_type == SPACE_VIEW3D) &&
 +                       (tref->mode == CTX_MODE_GPENCIL_WEIGHT))
 +              {
 +                      const EnumPropertyItem *items = rna_enum_gpencil_weight_brush_items;
 +                      const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
 +                      if (i != -1) {
 +                              const int value = items[i].value;
 +                              wmWindowManager *wm = bmain->wm.first;
 +                              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                                      if (workspace == WM_window_get_active_workspace(win)) {
 +                                              Scene *scene = WM_window_get_active_scene(win);
 +                                              ToolSettings *ts = scene->toolsettings;
 +                                              ts->gp_sculpt.weighttype = value;
 +                                      }
 +                              }
 +                      }
 +              }
 +              else if ((tref->space_type == SPACE_VIEW3D) &&
 +                       (tref->mode == CTX_MODE_PARTICLE))
 +              {
 +                      const EnumPropertyItem *items = rna_enum_particle_edit_hair_brush_items;
 +                      const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
 +                      if (i != -1) {
 +                              const int value = items[i].value;
 +                              wmWindowManager *wm = bmain->wm.first;
 +                              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                                      if (workspace == WM_window_get_active_workspace(win)) {
 +                                              Scene *scene = WM_window_get_active_scene(win);
 +                                              ToolSettings *ts = scene->toolsettings;
 +                                              ts->particle.brushtype = value;
 +                                      }
 +                              }
 +                      }
 +              }
 +              else if ((tref->space_type == SPACE_IMAGE) &&
 +                       (tref->mode == SI_MODE_UV))
 +              {
 +                      /* Note that switching uv-sculpt boolean is a hack at the moment.
 +                       * It would be best to make this either an operator or a higher level mode (like mesh-object sculpt mode). */
 +                      const EnumPropertyItem *items = rna_enum_uv_sculpt_tool_items;
 +                      const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
 +                      if (i != -1) {
 +                              const int value = items[i].value;
 +                              wmWindowManager *wm = bmain->wm.first;
 +                              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                                      if (workspace == WM_window_get_active_workspace(win)) {
 +                                              Scene *scene = WM_window_get_active_scene(win);
 +                                              ToolSettings *ts = scene->toolsettings;
 +                                              ts->uv_sculpt_tool = value;
 +
 +                                              if (ts->use_uv_sculpt == false) {
 +                                                      ts->use_uv_sculpt = true;
 +                                                      toolsystem_ref_link__refresh_image_uv_sculpt(C, scene);
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +              else {
 +                      const ePaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
++                      BLI_assert(paint_mode != PAINT_MODE_INVALID);
 +                      const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
 +                      BLI_assert(items != NULL);
 +
 +                      const int i = items ? RNA_enum_from_identifier(items, tref_rt->data_block) : -1;
 +                      if (i != -1) {
 +                              const int slot_index = items[i].value;
 +                              wmWindowManager *wm = bmain->wm.first;
 +                              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                                      if (workspace == WM_window_get_active_workspace(win)) {
 +                                              Scene *scene = WM_window_get_active_scene(win);
 +                                              Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
 +                                              struct Brush *brush = BKE_paint_toolslots_brush_get(paint, slot_index);
 +                                              if (brush == NULL) {
 +                                                      /* Could make into a function. */
 +                                                      brush = (struct Brush *)BKE_libblock_find_name(bmain, ID_BR, items[i].name);
 +                                                      if (brush && slot_index == BKE_brush_tool_get(brush, paint)) {
 +                                                              /* pass */
 +                                                      }
 +                                                      else {
 +                                                              brush = BKE_brush_add(bmain, items[i].name, paint->runtime.ob_mode);
 +                                                              BKE_brush_tool_set(brush, paint, slot_index);
 +                                                      }
 +                                                      BKE_paint_brush_set(paint, brush);
 +                                              }
 +                                              BKE_paint_brush_set(paint, brush);
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +      else {
 +              /* XXX, this part is weak, disables uv_sculpt when non uv-tool set. */
 +              if ((tref->space_type == SPACE_IMAGE) &&
 +                  (tref->mode == SI_MODE_UV))
 +              {
 +                      Main *bmain = CTX_data_main(C);
 +                      wmWindowManager *wm = bmain->wm.first;
 +                      for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                              if (workspace == WM_window_get_active_workspace(win)) {
 +                                      Scene *scene = WM_window_get_active_scene(win);
 +                                      ToolSettings *ts = scene->toolsettings;
 +                                      if (ts->use_uv_sculpt == true) {
 +                                              ts->use_uv_sculpt = false;
 +                                              toolsystem_ref_link__refresh_image_uv_sculpt(C, scene);
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +static void toolsystem_refresh_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
 +{
 +      if (tref->runtime == NULL) {
 +              return;
 +      }
 +      /* currently same operation. */
 +      toolsystem_ref_link(C, workspace, tref);
 +}
 +void WM_toolsystem_refresh(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
 +{
 +      bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
 +      if (tref) {
 +              toolsystem_refresh_ref(C, workspace, tref);
 +      }
 +}
 +
 +static void toolsystem_reinit_ref(bContext *C, WorkSpace *workspace, bToolRef *tref)
 +{
 +      toolsystem_reinit_with_toolref(C, workspace, tref);
 +}
 +void WM_toolsystem_reinit(bContext *C, WorkSpace *workspace, const bToolKey *tkey)
 +{
 +      bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
 +      if (tref) {
 +              toolsystem_reinit_ref(C, workspace, tref);
 +      }
 +}
 +
 +/* Operate on all active tools. */
 +void WM_toolsystem_unlink_all(struct bContext *C, struct WorkSpace *workspace)
 +{
 +      LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
 +              tref->tag = 0;
 +      }
 +
 +      LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
 +              if (tref->runtime) {
 +                      if (tref->tag == 0) {
 +                              toolsystem_unlink_ref(C, workspace, tref);
 +                              tref->tag = 1;
 +                      }
 +              }
 +      }
 +}
 +
 +void WM_toolsystem_refresh_all(struct bContext *C, struct WorkSpace *workspace)
 +{
 +      BLI_assert(0);
 +      LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
 +              toolsystem_refresh_ref(C, workspace, tref);
 +      }
 +}
 +void WM_toolsystem_reinit_all(struct bContext *C, wmWindow *win)
 +{
 +      bScreen *screen = WM_window_get_active_screen(win);
 +      ViewLayer *view_layer = WM_window_get_active_view_layer(win);
 +      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +              if (((1 << sa->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) == 0) {
 +                      continue;
 +              }
 +
 +              WorkSpace *workspace = WM_window_get_active_workspace(win);
 +              const bToolKey tkey = {
 +                      .space_type = sa->spacetype,
 +                      .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
 +              };
 +              bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
 +              if (tref) {
 +                      if (tref->tag == 0) {
 +                              toolsystem_reinit_ref(C, workspace, tref);
 +                              tref->tag = 1;
 +                      }
 +              }
 +      }
 +}
 +
 +void WM_toolsystem_ref_set_from_runtime(
 +        struct bContext *C, struct WorkSpace *workspace, bToolRef *tref,
 +        const bToolRef_Runtime *tref_rt, const char *idname)
 +{
 +      Main *bmain = CTX_data_main(C);
 +
 +      if (tref->runtime) {
 +              toolsystem_unlink_ref(C, workspace, tref);
 +      }
 +
 +      STRNCPY(tref->idname, idname);
 +
 +      /* BAD DESIGN WARNING: used for topbar. */
 +      workspace->tools_space_type = tref->space_type;
 +      workspace->tools_mode = tref->mode;
 +
 +      if (tref->runtime == NULL) {
 +              tref->runtime = MEM_callocN(sizeof(*tref->runtime), __func__);
 +      }
 +
 +      if (tref_rt != tref->runtime) {
 +              *tref->runtime = *tref_rt;
 +      }
 +
 +      toolsystem_ref_link(C, workspace, tref);
 +
 +      toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref);
 +
 +      {
 +              struct wmMsgBus *mbus = CTX_wm_message_bus(C);
 +              WM_msg_publish_rna_prop(
 +                      mbus, &workspace->id, workspace, WorkSpace, tools);
 +      }
 +}
 +
 +/**
 + * Sync the internal active state of a tool back into the tool system,
 + * this is needed for active brushes where the real active state is not stored in the tool system.
 + *
 + * \see #toolsystem_ref_link
 + */
 +void WM_toolsystem_ref_sync_from_context(
 +        Main *bmain, WorkSpace *workspace, bToolRef *tref)
 +{
 +      bToolRef_Runtime *tref_rt = tref->runtime;
 +      if ((tref_rt == NULL) || (tref_rt->data_block[0] == '\0')) {
 +              return;
 +      }
 +      wmWindowManager *wm = bmain->wm.first;
 +      for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +              if (workspace != WM_window_get_active_workspace(win)) {
 +                      continue;
 +              }
 +
 +              Scene *scene = WM_window_get_active_scene(win);
 +              ToolSettings *ts = scene->toolsettings;
 +              const ViewLayer *view_layer = WM_window_get_active_view_layer(win);
 +              const Object *ob = OBACT(view_layer);
 +              if (ob == NULL) {
 +                      /* pass */
 +              }
 +              else if ((tref->space_type == SPACE_VIEW3D) &&
 +                       (tref->mode == CTX_MODE_GPENCIL_SCULPT))
 +              {
 +                      if (ob->mode & OB_MODE_GPENCIL_SCULPT) {
 +                              const EnumPropertyItem *items = rna_enum_gpencil_sculpt_brush_items;
 +                              const int i = RNA_enum_from_value(items, ts->gp_sculpt.brushtype);
 +                              const EnumPropertyItem *item = &items[i];
 +                              if (!STREQ(tref_rt->data_block, item->identifier)) {
 +                                      STRNCPY(tref_rt->data_block, item->identifier);
 +                                      STRNCPY(tref->idname, item->name);
 +                              }
 +                      }
 +              }
 +              else if ((tref->space_type == SPACE_VIEW3D) &&
 +                       (tref->mode == CTX_MODE_GPENCIL_WEIGHT))
 +              {
 +                      if (ob->mode & OB_MODE_GPENCIL_WEIGHT) {
 +                              const EnumPropertyItem *items = rna_enum_gpencil_weight_brush_items;
 +                              const int i = RNA_enum_from_value(items, ts->gp_sculpt.weighttype);
 +                              const EnumPropertyItem *item = &items[i];
 +                              if (!STREQ(tref_rt->data_block, item->identifier)) {
 +                                      STRNCPY(tref_rt->data_block, item->identifier);
 +                                      STRNCPY(tref->idname, item->name);
 +                              }
 +                      }
 +              }
 +              else if ((tref->space_type == SPACE_VIEW3D) &&
 +                       (tref->mode == CTX_MODE_PARTICLE))
 +              {
 +                      if (ob->mode & OB_MODE_PARTICLE_EDIT) {
 +                              const EnumPropertyItem *items = rna_enum_particle_edit_hair_brush_items;
 +                              const int i = RNA_enum_from_value(items, ts->particle.brushtype);
 +                              const EnumPropertyItem *item = &items[i];
 +                              if (!STREQ(tref_rt->data_block, item->identifier)) {
 +                                      STRNCPY(tref_rt->data_block, item->identifier);
 +                                      STRNCPY(tref->idname, item->name);
 +                              }
 +                      }
 +              }
 +              else if ((tref->space_type == SPACE_IMAGE) &&
 +                       (tref->mode == SI_MODE_UV))
 +              {
 +                      if (ob->mode & OB_MODE_EDIT) {
 +                              const EnumPropertyItem *items = rna_enum_uv_sculpt_tool_items;
 +                              const int i = RNA_enum_from_value(items, ts->uv_sculpt_tool);
 +                              const EnumPropertyItem *item = &items[i];
 +                              if (!STREQ(tref_rt->data_block, item->identifier)) {
 +                                      STRNCPY(tref_rt->data_block, item->identifier);
 +                                      STRNCPY(tref->idname, item->name);
 +                              }
 +                      }
 +              }
 +              else {
 +                      const ePaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
 +                      Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
 +                      const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
 +                      if (paint && paint->brush && items) {
 +                              const ID *brush = (ID *)paint->brush;
 +                              const char tool_type = BKE_brush_tool_get((struct Brush *)brush, paint);
 +                              const int i = RNA_enum_from_value(items, tool_type);
 +                              /* Possible when loading files from the future. */
 +                              if (i != -1) {
 +                                      const char *name = items[i].name;
 +                                      const char *identifier = items[i].identifier;
 +                                      if (!STREQ(tref_rt->data_block, identifier)) {
 +                                              STRNCPY(tref_rt->data_block, identifier);
 +                                              STRNCPY(tref->idname, name);
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +void WM_toolsystem_init(bContext *C)
 +{
 +      Main *bmain = CTX_data_main(C);
 +
 +      BLI_assert(CTX_wm_window(C) == NULL);
 +
 +      LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
 +              LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
 +                      MEM_SAFE_FREE(tref->runtime);
 +                      tref->tag = 0;
 +              }
 +      }
 +
 +      for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
 +              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                      CTX_wm_window_set(C, win);
 +                      WorkSpace *workspace = WM_window_get_active_workspace(win);
 +                      bScreen *screen = WM_window_get_active_screen(win);
 +                      ViewLayer *view_layer = WM_window_get_active_view_layer(win);
 +                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                              if (((1 << sa->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) == 0) {
 +                                      continue;
 +                              }
 +                              const bToolKey tkey = {
 +                                      .space_type = sa->spacetype,
 +                                      .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
 +                              };
 +                              bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
 +                              if (tref) {
 +                                      if (tref->tag == 0) {
 +                                              toolsystem_reinit_ref(C, workspace, tref);
 +                                              tref->tag = 1;
 +                                      }
 +                              }
 +                              else {
 +                                      /* Without this we may load a file without a default tool. */
 +                                      tref = toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
 +                                      tref->tag = 1;
 +                              }
 +                      }
 +                      CTX_wm_window_set(C, NULL);
 +              }
 +      }
 +}
 +
 +int WM_toolsystem_mode_from_spacetype(
 +        ViewLayer *view_layer, ScrArea *sa, int spacetype)
 +{
 +      int mode = -1;
 +      switch (spacetype) {
 +              case SPACE_VIEW3D:
 +              {
 +                      /* 'sa' may be NULL in this case. */
 +                      Object *obact = OBACT(view_layer);
 +                      if (obact != NULL) {
 +                              Object *obedit = OBEDIT_FROM_OBACT(obact);
 +                              mode = CTX_data_mode_enum_ex(obedit, obact, obact->mode);
 +                      }
 +                      else {
 +                              mode = CTX_MODE_OBJECT;
 +                      }
 +                      break;
 +              }
 +              case SPACE_IMAGE:
 +              {
 +                      SpaceImage *sima = sa->spacedata.first;
 +                      mode = sima->mode;
 +                      break;
 +              }
 +      }
 +      return mode;
 +}
 +
 +bool WM_toolsystem_key_from_context(
 +        ViewLayer *view_layer, ScrArea *sa, bToolKey *tkey)
 +{
 +      int space_type = SPACE_EMPTY;
 +      int mode = -1;
 +
 +      if (sa != NULL) {
 +              space_type = sa->spacetype;
 +              mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, space_type);
 +      }
 +
 +      if (mode != -1) {
 +              tkey->space_type = space_type;
 +              tkey->mode = mode;
 +              return true;
 +      }
 +      return false;
 +}
 +
 +/**
 + * Use to update the active tool (shown in the top bar) in the least disruptive way.
 + *
 + * This is a little involved since there may be multiple valid active tools depending on the mode and space type.
 + *
 + * Used when undoing since the active mode may have changed.
 + */
 +void WM_toolsystem_refresh_active(bContext *C)
 +{
 +      Main *bmain = CTX_data_main(C);
 +      for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
 +              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                      WorkSpace *workspace = WM_window_get_active_workspace(win);
 +                      bScreen *screen = WM_window_get_active_screen(win);
 +                      ViewLayer *view_layer = WM_window_get_active_view_layer(win);
 +                      int mode_other = 0;
 +                      enum { UNSET = -1, CHANGE = 0, MATCH = 1 } mode_match = UNSET;
 +                      /* Could skip loop for modes that don't depend on space type. */
 +                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                              /* Don't change the space type of the active tool, only update it's mode. */
 +                              if (sa->spacetype == workspace->tools_space_type) {
 +                                      const int mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype);
 +                                      if (workspace->tools_mode == mode) {
 +                                              mode_match = MATCH;
 +                                              break;
 +                                      }
 +                                      else if (mode_match == -1) {
 +                                              mode_match = CHANGE;
 +                                              mode_other = mode;
 +                                      }
 +                              }
 +                      }
 +
 +                      if (mode_match == CHANGE) {
 +                              const bToolKey tkey = {
 +                                      .space_type = workspace->tools_space_type,
 +                                      .mode = mode_other,
 +                              };
 +                              toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
 +                      }
 +              }
 +      }
 +}
 +
 +void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, ViewLayer *view_layer, ScrArea *sa)
 +{
 +      sa->runtime.tool = NULL;
 +      sa->runtime.is_tool_set = true;
 +      const int mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype);
 +      for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) {
 +              if (tref->space_type == sa->spacetype) {
 +                      if (tref->mode == mode) {
 +                              sa->runtime.tool = tref;
 +                              break;
 +                      }
 +              }
 +      }
 +}
 +
 +void WM_toolsystem_refresh_screen_all(Main *bmain)
 +{
 +      /* Update all ScrArea's tools */
 +      for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
 +              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                      WorkSpace *workspace = WM_window_get_active_workspace(win);
 +                      bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0};
 +                      for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) {
 +                              space_type_has_tools[tref->space_type] = true;
 +                      }
 +                      bScreen *screen = WM_window_get_active_screen(win);
 +                      ViewLayer *view_layer = WM_window_get_active_view_layer(win);
 +                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                              sa->runtime.tool = NULL;
 +                              sa->runtime.is_tool_set = true;
 +                              if (space_type_has_tools[sa->spacetype]) {
 +                                      WM_toolsystem_refresh_screen_area(workspace, view_layer, sa);
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +static void toolsystem_refresh_screen_from_active_tool(
 +        Main *bmain, WorkSpace *workspace, bToolRef *tref)
 +{
 +      /* Update all ScrArea's tools */
 +      for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
 +              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                      if (workspace == WM_window_get_active_workspace(win)) {
 +                              bScreen *screen = WM_window_get_active_screen(win);
 +                              ViewLayer *view_layer = WM_window_get_active_view_layer(win);
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      if (sa->spacetype == tref->space_type) {
 +                                              int mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype);
 +                                              if (mode == tref->mode) {
 +                                                      sa->runtime.tool = tref;
 +                                                      sa->runtime.is_tool_set = true;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +bToolRef *WM_toolsystem_ref_set_by_name(
 +        bContext *C, WorkSpace *workspace, const bToolKey *tkey,
 +        const char *name, bool cycle)
 +{
 +      wmOperatorType *ot = WM_operatortype_find("WM_OT_tool_set_by_name", false);
 +      /* On startup, Python operatores are not yet loaded. */
 +      if (ot == NULL) {
 +              return NULL;
 +      }
 +      PointerRNA op_props;
 +      WM_operator_properties_create_ptr(&op_props, ot);
 +      RNA_string_set(&op_props, "name", name);
 +
 +      /* Will get from context if not set. */
 +      bToolKey tkey_from_context;
 +      if (tkey == NULL) {
 +              ViewLayer *view_layer = CTX_data_view_layer(C);
 +              ScrArea *sa = CTX_wm_area(C);
 +              WM_toolsystem_key_from_context(view_layer, sa, &tkey_from_context);
 +              tkey = &tkey_from_context;
 +      }
 +
 +      BLI_assert((1 << tkey->space_type) & WM_TOOLSYSTEM_SPACE_MASK);
 +
 +      RNA_enum_set(&op_props, "space_type", tkey->space_type);
 +      RNA_boolean_set(&op_props, "cycle", cycle);
 +
 +      WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props);
 +      WM_operator_properties_free(&op_props);
 +
 +      bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey);
 +
 +      if (tref) {
 +              Main *bmain = CTX_data_main(C);
 +              toolsystem_refresh_screen_from_active_tool(bmain, workspace, tref);
 +      }
 +
 +      return (tref && STREQ(tref->idname, name)) ? tref : NULL;
 +}
 +
 +static void toolsystem_reinit_with_toolref(
 +        bContext *C, WorkSpace *workspace, bToolRef *tref)
 +{
 +      bToolKey tkey = {
 +              .space_type = tref->space_type,
 +              .mode = tref->mode,
 +      };
 +      WM_toolsystem_ref_set_by_name(C, workspace, &tkey, tref->idname, false);
 +}
 +
 +static const char *toolsystem_default_tool(const bToolKey *tkey)
 +{
 +      switch (tkey->space_type) {
 +              case SPACE_VIEW3D:
 +                      switch (tkey->mode) {
 +                              /* Use the names of the enums for each brush tool. */
 +                              case CTX_MODE_SCULPT:
 +                              case CTX_MODE_PAINT_VERTEX:
 +                              case CTX_MODE_PAINT_WEIGHT:
 +                              case CTX_MODE_GPENCIL_WEIGHT:
 +                              case CTX_MODE_PAINT_TEXTURE:
 +                              case CTX_MODE_GPENCIL_PAINT:
 +                                      return "Draw";
 +                              case CTX_MODE_GPENCIL_SCULPT:
 +                                      return "Push";
 +                              /* end temporary hack. */
 +
 +                              case CTX_MODE_PARTICLE:
 +                                      return "Comb";
 +                              default:
 +                                      /* FIXME(campbell): disable for now since this means we can't lasso select by default. */
 +#if 0
 +                                      return "Select Box";
 +#endif
 +                                      break;
 +                      }
 +                      break;
 +      }
 +
 +      return "Cursor";
 +}
 +
 +/**
 + * Run after changing modes.
 + */
 +static bToolRef *toolsystem_reinit_ensure_toolref(
 +        bContext *C, WorkSpace *workspace, const bToolKey *tkey, const char *default_tool)
 +{
 +      bToolRef *tref;
 +      if (WM_toolsystem_ref_ensure(workspace, tkey, &tref)) {
 +              if (default_tool == NULL) {
 +                      default_tool = toolsystem_default_tool(tkey);
 +              }
 +              STRNCPY(tref->idname, default_tool);
 +      }
 +      toolsystem_reinit_with_toolref(C, workspace, tref);
 +      return tref;
 +}
 +
 +void WM_toolsystem_update_from_context_view3d(bContext *C)
 +{
 +      WorkSpace *workspace = CTX_wm_workspace(C);
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      int space_type = SPACE_VIEW3D;
 +      const bToolKey tkey = {
 +              .space_type = space_type,
 +              .mode = WM_toolsystem_mode_from_spacetype(view_layer, NULL, space_type),
 +      };
 +      toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
 +}
 +
 +/**
 + * For paint modes to support non-brush tools.
 + */
 +bool WM_toolsystem_active_tool_is_brush(const bContext *C)
 +{
 +      bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
 +      return tref_rt && (tref_rt->data_block[0] != '\0');
 +}
 +
 +/* Follow wmMsgNotifyFn spec */
 +void WM_toolsystem_do_msg_notify_tag_refresh(
 +        bContext *C, wmMsgSubscribeKey *UNUSED(msg_key), wmMsgSubscribeValue *msg_val)
 +{
 +      WorkSpace *workspace = CTX_wm_workspace(C);
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      ScrArea *sa = msg_val->user_data;
 +      int space_type = sa->spacetype;
 +      const bToolKey tkey = {
 +              .space_type = space_type,
 +              .mode = WM_toolsystem_mode_from_spacetype(view_layer, sa, sa->spacetype),
 +      };
 +      WM_toolsystem_refresh(C, workspace, &tkey);
 +}
 +
 +IDProperty *WM_toolsystem_ref_properties_ensure_idprops(bToolRef *tref)
 +{
 +      if (tref->properties == NULL) {
 +              IDPropertyTemplate val = {0};
 +              tref->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
 +      }
 +      return tref->properties;
 +}
 +
 +
 +void WM_toolsystem_ref_properties_ensure_ex(bToolRef *tref, const char *idname, StructRNA *type, PointerRNA *r_ptr)
 +{
 +      IDProperty *group = WM_toolsystem_ref_properties_ensure_idprops(tref);
 +      IDProperty *prop = IDP_GetPropertyFromGroup(group, idname);
 +      if (prop == NULL) {
 +              IDPropertyTemplate val = {0};
 +              prop = IDP_New(IDP_GROUP, &val, "wmGenericProperties");
 +              STRNCPY(prop->name, idname);
 +              IDP_ReplaceInGroup_ex(group, prop, NULL);
 +      }
 +      else {
 +              BLI_assert(prop->type == IDP_GROUP);
 +      }
 +
 +      RNA_pointer_create(NULL, type, prop, r_ptr);
 +}
 +
 +void WM_toolsystem_ref_properties_init_for_keymap(
 +        bToolRef *tref, PointerRNA *dst_ptr, PointerRNA *src_ptr, wmOperatorType *ot)
 +{
 +      *dst_ptr = *src_ptr;
 +      if (dst_ptr->data) {
 +              dst_ptr->data = IDP_CopyProperty(dst_ptr->data);
 +      }
 +      else {
 +              IDPropertyTemplate val = {0};
 +              dst_ptr->data = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
 +      }
 +      if (tref->properties != NULL) {
 +              IDProperty *prop = IDP_GetPropertyFromGroup(tref->properties, ot->idname);
 +              if (prop) {
 +                      IDP_MergeGroup(dst_ptr->data, prop, true);
 +              }
 +      }
 +}