Merge branch 'master' into blender2.8
authorBastien Montagne <montagne29@wanadoo.fr>
Fri, 8 Jun 2018 12:38:57 +0000 (14:38 +0200)
committerBastien Montagne <montagne29@wanadoo.fr>
Fri, 8 Jun 2018 12:38:57 +0000 (14:38 +0200)
Conflicts:
source/blender/blenkernel/BKE_sequencer.h
source/blender/blenkernel/intern/sequencer.c
source/blender/editors/curve/editcurve_paint.c
source/blender/editors/gpencil/gpencil_edit.c
source/blender/editors/gpencil/gpencil_paint.c
source/blender/editors/gpencil/gpencil_utils.c
source/blender/editors/include/ED_object.h
source/blender/editors/include/ED_view3d.h
source/blender/editors/interface/interface_eyedropper_depth.c
source/blender/editors/render/render_opengl.c
source/blender/editors/sculpt_paint/paint_image_proj.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/space_view3d/drawobject.c
source/blender/editors/space_view3d/view3d_draw.c
source/blender/editors/space_view3d/view3d_edit.c
source/blender/editors/space_view3d/view3d_intern.h
source/blender/editors/space_view3d/view3d_select.c
source/blender/editors/space_view3d/view3d_utils.c
source/blender/editors/transform/transform_conversions.c
source/blender/editors/transform/transform_snap.c
source/blender/python/intern/gpu_offscreen.c
source/blender/windowmanager/intern/wm_files.c

29 files changed:
1  2 
CMakeLists.txt
source/blender/editors/curve/editcurve.c
source/blender/editors/curve/editcurve_paint.c
source/blender/editors/gpencil/gpencil_edit.c
source/blender/editors/gpencil/gpencil_paint.c
source/blender/editors/gpencil/gpencil_utils.c
source/blender/editors/include/ED_object.h
source/blender/editors/include/ED_transform_snap_object_context.h
source/blender/editors/include/ED_view3d.h
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface.c
source/blender/editors/interface/interface_eyedropper_depth.c
source/blender/editors/interface/interface_intern.h
source/blender/editors/interface/resources.c
source/blender/editors/mesh/editmesh_tools.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/space_view3d/view3d_edit.c
source/blender/editors/space_view3d/view3d_intern.h
source/blender/editors/space_view3d/view3d_manipulator_ruler.c
source/blender/editors/space_view3d/view3d_ruler.c
source/blender/editors/space_view3d/view3d_select.c
source/blender/editors/space_view3d/view3d_utils.c
source/blender/editors/space_view3d/view3d_walk.c
source/blender/editors/transform/transform_conversions.c
source/blender/editors/transform/transform_snap.c
source/blender/editors/transform/transform_snap_object.c
source/blender/makesrna/intern/rna_scene_api.c
source/blender/python/intern/gpu_offscreen.c
source/blender/windowmanager/intern/wm_files.c

diff --cc CMakeLists.txt
Simple merge
@@@ -5026,16 -5009,17 +5026,16 @@@ static int add_vertex_invoke(bContext *
                        const float mval[2] = {UNPACK2(event->mval)};
  
                        struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d(
-                               vc.scene, CTX_data_depsgraph(C), 0, vc.ar, vc.v3d);
 -                              CTX_data_main(C), vc.scene, 0,
 -                              vc.ar, vc.v3d);
++                              vc.bmain, vc.scene, vc.depsgraph, 0, vc.ar, vc.v3d);
  
 -                      ED_transform_snap_object_project_view3d_mixed(
 +                      ED_transform_snap_object_project_view3d(
                                snap_context,
 -                              SCE_SELECT_FACE,
 +                              SCE_SNAP_MODE_FACE,
                                &(const struct SnapObjectParams){
 -                                  .snap_select = (vc.scene->obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL,
 +                                  .snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL,
                                    .use_object_edit_cage = false,
                                },
 -                              mval, NULL, true,
 +                              mval, NULL,
                                location, NULL);
  
  
  
  #include "BKE_context.h"
  #include "BKE_curve.h"
 -#include "BKE_depsgraph.h"
  #include "BKE_fcurve.h"
+ #include "BKE_main.h"
  #include "BKE_report.h"
  
 +#include "DEG_depsgraph.h"
 +
  #include "WM_api.h"
  #include "WM_types.h"
  
@@@ -616,9 -590,8 +617,10 @@@ static bool curve_draw_init(bContext *C
                }
        }
        else {
+               cdd->vc.bmain = CTX_data_main(C);
 +              cdd->vc.depsgraph = CTX_data_depsgraph(C);
                cdd->vc.scene = CTX_data_scene(C);
 +              cdd->vc.view_layer = CTX_data_view_layer(C);
                cdd->vc.obedit = CTX_data_edit_object(C);
        }
  
@@@ -115,8 -112,8 +116,9 @@@ typedef enum eGPencil_PaintFlags 
   *   "p" = op->customdata
   */
  typedef struct tGPsdata {
+       Main *bmain;
        Scene *scene;       /* current scene from context */
 +      struct Depsgraph *depsgraph;
  
        wmWindow *win;      /* window where painting originated */
        ScrArea *sa;        /* area where painting originated */
@@@ -1399,8 -1395,8 +1401,9 @@@ static bool gp_session_initdata(bContex
        }
  
        /* pass on current scene and window */
+       p->bmain = CTX_data_main(C);
        p->scene = CTX_data_scene(C);
 +      p->depsgraph = CTX_data_depsgraph(C);
        p->win = CTX_wm_window(C);
  
        unit_m4(p->imat);
@@@ -151,8 -140,7 +151,8 @@@ void ED_object_wpaintmode_exit_ex(struc
  void ED_object_wpaintmode_exit(struct bContext *C);
  
  void ED_object_sculptmode_enter_ex(
-         struct Depsgraph *depsgraph,
 -        struct Main *bmain, struct Scene *scene, struct Object *ob,
++        struct Main *bmain, struct Depsgraph *depsgraph,
 +        struct Scene *scene, struct Object *ob,
          struct ReportList *reports);
  void ED_object_sculptmode_enter(struct bContext *C, struct ReportList *reports);
  void ED_object_sculptmode_exit_ex(
@@@ -79,9 -73,9 +79,9 @@@ struct SnapObjectParams 
  
  typedef struct SnapObjectContext SnapObjectContext;
  SnapObjectContext *ED_transform_snap_object_context_create(
-         struct Scene *scene, struct Depsgraph *depsgraph, int flag);
 -        struct Main *bmain, struct Scene *scene, int flag);
++        struct Main *bmain, struct Scene *scene, struct Depsgraph *depsgraph, int flag);
  SnapObjectContext *ED_transform_snap_object_context_create_view3d(
-         struct Scene *scene, struct Depsgraph *depsgraph, int flag,
 -        struct Main *bmain, struct Scene *scene, int flag,
++        struct Main *bmain, struct Scene *scene, struct Depsgraph *depsgraph, int flag,
          /* extra args for view3d */
          const struct ARegion *ar, const struct View3D *v3d);
  void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx);
@@@ -75,9 -70,8 +75,10 @@@ enum eGPUFXFlags
  
  /* for derivedmesh drawing callbacks, for view3d_select, .... */
  typedef struct ViewContext {
+       struct Main *bmain;
 +      struct Depsgraph *depsgraph;
        struct Scene *scene;
 +      struct ViewLayer *view_layer;
        struct Object *obact;
        struct Object *obedit;
        struct ARegion *ar;
@@@ -331,7 -304,7 +332,8 @@@ bool ED_view3d_autodist
          const bool alphaoverride, const float fallback_depth_pt[3]);
  
  /* only draw so ED_view3d_autodist_simple can be called many times after */
- void ED_view3d_autodist_init(struct Depsgraph *depsgraph, struct ARegion *ar, struct View3D *v3d, int mode);
 -void ED_view3d_autodist_init(struct Main *bmain, struct Scene *scene, struct ARegion *ar, struct View3D *v3d, int mode);
++void ED_view3d_autodist_init(
++        struct Depsgraph *depsgraph, struct ARegion *ar, struct View3D *v3d, int mode);
  bool ED_view3d_autodist_simple(struct ARegion *ar, const int mval[2], float mouse_worldloc[3], int margin, float *force_depth);
  bool ED_view3d_autodist_depth(struct ARegion *ar, const int mval[2], int margin, float *depth);
  bool ED_view3d_autodist_depth_seg(struct ARegion *ar, const int mval_sta[2], const int mval_end[2], int margin, float *depth);
@@@ -860,8 -807,9 +860,8 @@@ void UI_popup_handlers_remove_all(struc
   * be used to reinitialize some internal state if user preferences change. */
  
  void UI_init(void);
- void UI_init_userdef(void);
+ void UI_init_userdef(struct Main *bmain);
  void UI_reinit_font(void);
 -void UI_reinit_gl_state(void);
  void UI_exit(void);
  
  /* Layout
  
  #include "BLI_utildefines.h"
  
 +#include "BKE_animsys.h"
  #include "BKE_context.h"
- #include "BKE_unit.h"
+ #include "BKE_idprop.h"
+ #include "BKE_main.h"
  #include "BKE_scene.h"
  #include "BKE_screen.h"
- #include "BKE_idprop.h"
+ #include "BKE_unit.h"
  
 -#include "BIF_gl.h"
 +#include "GPU_glew.h"
 +#include "GPU_matrix.h"
  
  #include "BLF_api.h"
  #include "BLT_translation.h"
@@@ -359,6 -314,6 +359,7 @@@ void MESH_OT_unsubdivide(wmOperatorTyp
  
  void EMBM_project_snap_verts(bContext *C, ARegion *ar, BMEditMesh *em)
  {
++      Main *bmain = CTX_data_main(C);
        Object *obedit = em->ob;
        BMIter iter;
        BMVert *eve;
        ED_view3d_init_mats_rv3d(obedit, ar->regiondata);
  
        struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d(
-               CTX_data_scene(C), CTX_data_depsgraph(C), 0,
 -              CTX_data_main(C), CTX_data_scene(C), 0,
++              bmain, CTX_data_scene(C), CTX_data_depsgraph(C), 0,
                ar, CTX_wm_view3d(C));
  
        BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
@@@ -5643,8 -5630,7 +5643,8 @@@ static int ed_object_sculptmode_flush_r
  }
  
  void ED_object_sculptmode_enter_ex(
-         Depsgraph *depsgraph,
 -        Main *bmain, Scene *scene, Object *ob,
++        Main *bmain, Depsgraph *depsgraph,
 +        Scene *scene, Object *ob,
          ReportList *reports)
  {
        const int mode_flag = OB_MODE_SCULPT;
  
  void ED_object_sculptmode_enter(struct bContext *C, ReportList *reports)
  {
+       Main *bmain = CTX_data_main(C);
        Scene *scene = CTX_data_scene(C);
        Object *ob = CTX_data_active_object(C);
 -      ED_object_sculptmode_enter_ex(bmain, scene, ob, reports);
 +      Depsgraph *depsgraph = CTX_data_depsgraph(C);
-       ED_object_sculptmode_enter_ex(depsgraph, scene, ob, reports);
++      ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, reports);
  }
  
  void ED_object_sculptmode_exit_ex(
@@@ -5814,8 -5791,7 +5815,9 @@@ void ED_object_sculptmode_exit(bContex
  
  static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
  {
 +      struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+       Main *bmain = CTX_data_main(C);
 +      Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C);
        Scene *scene = CTX_data_scene(C);
        Object *ob = CTX_data_active_object(C);
        const int mode_flag = OB_MODE_SCULPT;
        }
  
        if (is_mode_set) {
 -              ED_object_sculptmode_exit_ex(scene, ob);
 +              ED_object_sculptmode_exit_ex(depsgraph, scene, ob);
        }
        else {
-               ED_object_sculptmode_enter_ex(depsgraph, scene, ob, op->reports);
 -              ED_object_sculptmode_enter_ex(bmain, scene, ob, op->reports);
++              ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, op->reports);
        }
  
        WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene);
@@@ -221,7 -217,7 +222,8 @@@ static void viewops_data_alloc(bContex
  
        /* store data */
        op->customdata = vod;
+       vod->bmain = CTX_data_main(C);
 +      vod->depsgraph = CTX_data_depsgraph(C);
        vod->scene = CTX_data_scene(C);
        vod->sa = CTX_wm_area(C);
        vod->ar = CTX_wm_region(C);
@@@ -4592,67 -4568,15 +4594,68 @@@ void ED_view3d_cursor3d_position(bConte
  
  void ED_view3d_cursor3d_update(bContext *C, const int mval[2])
  {
++      Main *bmain = CTX_data_main(C);
        Scene *scene = CTX_data_scene(C);
        View3D *v3d = CTX_wm_view3d(C);
 +      ARegion *ar = CTX_wm_region(C);
 +      RegionView3D *rv3d = ar->regiondata;
  
 -      float *fp_curr = ED_view3d_cursor3d_get(scene, v3d);
 -      float  fp_prev[3];
 +      View3DCursor *cursor_curr = ED_view3d_cursor3d_get(scene, v3d);
 +      View3DCursor  cursor_prev = *cursor_curr;
  
 -      copy_v3_v3(fp_prev, fp_curr);
 +      ED_view3d_cursor3d_position(C, cursor_curr->location, mval);
 +      copy_qt_qt(cursor_curr->rotation, rv3d->viewquat);
 +      cursor_curr->rotation[0] *= -1.0f;
  
 -      ED_view3d_cursor3d_position(C, fp_curr, mval);
 +      {
 +              const float mval_fl[2] = {UNPACK2(mval)};
 +              float ray_no[3];
 +
 +              struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d(
-                       scene, CTX_data_depsgraph(C), 0, ar, v3d);
++                      bmain, scene, CTX_data_depsgraph(C), 0, ar, v3d);
 +
 +              float obmat[4][4];
 +              Object *ob_dummy = NULL;
 +              float dist_px = 0;
 +              if (ED_transform_snap_object_project_view3d_ex(
 +                      snap_context,
 +                      SCE_SNAP_MODE_FACE,
 +                      &(const struct SnapObjectParams){
 +                          .snap_select = SNAP_ALL,
 +                          .use_object_edit_cage = false,
 +                      },
 +                      mval_fl, &dist_px,
 +                      cursor_curr->location, ray_no, NULL,
 +                      &ob_dummy, obmat))
 +              {
 +                      float tquat[4];
 +                      /* Math normal (Z). */
 +                      {
 +                              float z_src[3] = {0, 0, 1};
 +                              mul_qt_v3(cursor_curr->rotation, z_src);
 +                              rotation_between_vecs_to_quat(tquat, z_src, ray_no);
 +                              mul_qt_qtqt(cursor_curr->rotation, tquat, cursor_curr->rotation);
 +                      }
 +
 +                      /* Match object matrix (X). */
 +                      {
 +                              const float ortho_axis_dot[3] = {
 +                                      dot_v3v3(ray_no, obmat[0]),
 +                                      dot_v3v3(ray_no, obmat[1]),
 +                                      dot_v3v3(ray_no, obmat[2]),
 +                              };
 +                              const int ortho_axis = axis_dominant_v3_ortho_single(ortho_axis_dot);
 +                              float x_src[3] = {1, 0, 0};
 +                              float x_dst[3];
 +                              mul_qt_v3(cursor_curr->rotation, x_src);
 +                              project_plane_v3_v3v3(x_dst, obmat[ortho_axis], ray_no);
 +                              normalize_v3(x_dst);
 +                              rotation_between_vecs_to_quat(tquat, x_src, x_dst);
 +                              mul_qt_qtqt(cursor_curr->rotation, tquat, cursor_curr->rotation);
 +                      }
 +              }
 +              ED_transform_snap_object_context_destroy(snap_context);
 +      }
  
        /* offset the cursor lock to avoid jumping to new offset */
        if (v3d->ob_centre_cursor) {
@@@ -132,10 -133,43 +132,11 @@@ void VIEW3D_OT_walk(struct wmOperatorTy
  /* view3d_ruler.c */
  void VIEW3D_OT_ruler(struct wmOperatorType *ot);
  
 -/* drawanim.c */
 -void draw_motion_paths_init(View3D *v3d, struct ARegion *ar);
 -void draw_motion_path_instance(Scene *scene,
 -                               struct Object *ob, struct bPoseChannel *pchan,
 -                               struct bAnimVizSettings *avs, struct bMotionPath *mpath);
 -void draw_motion_paths_cleanup(View3D *v3d);
 -
 -
 -
  /* drawobject.c */
 -void draw_object(
 -        struct Main *bmain, Scene *scene, struct ARegion *ar, View3D *v3d,
 -        Base *base, const short dflag);
 -void draw_object_select(
 -        struct Main *bmain, Scene *scene, ARegion *ar, View3D *v3d,
 -        Base *base, const short dflag);
 -
 -bool draw_glsl_material(Scene *scene, struct Object *ob, View3D *v3d, const char dt);
 -void draw_object_instance(Scene *scene, View3D *v3d, RegionView3D *rv3d, struct Object *ob, const char dt, int outline);
 -void draw_object_backbufsel(Scene *scene, View3D *v3d, RegionView3D *rv3d, struct Object *ob);
 -void drawaxes(const float viewmat_local[4][4], float size, char drawtype);
 -
 -void view3d_cached_text_draw_begin(void);
 -void view3d_cached_text_draw_add(const float co[3],
 -                                 const char *str, const size_t str_len,
 -                                 short xoffs, short flag, const unsigned char col[4]);
 -void view3d_cached_text_draw_end(View3D *v3d, ARegion *ar, bool depth_write);
 -
 -bool check_object_draw_texture(struct Scene *scene, struct View3D *v3d, const char drawtype);
 -
 -enum {
 -      V3D_CACHE_TEXT_ZBUF         = (1 << 0),
 -      V3D_CACHE_TEXT_WORLDSPACE   = (1 << 1),
 -      V3D_CACHE_TEXT_ASCII        = (1 << 2),
 -      V3D_CACHE_TEXT_GLOBALSPACE  = (1 << 3),
 -      V3D_CACHE_TEXT_LOCALCLIP    = (1 << 4)
 -};
 +void draw_object_backbufsel(
-         struct Depsgraph *depsgraph, Scene *scene, View3D *v3d, RegionView3D *rv3d, struct Object *ob,
++        struct Depsgraph *depsgraph, Scene *scene,
++        View3D *v3d, RegionView3D *rv3d, struct Object *ob,
 +        short select_mode);
  
  int view3d_effective_drawtype(const struct View3D *v3d);
  
index 9cde5ff,0000000..cd91869
mode 100644,000000..100644
--- /dev/null
@@@ -1,1096 -1,0 +1,1099 @@@
- #include "BKE_object.h"
 +/*
 + * ***** 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/editors/space_view3d/view3d_manipulator_ruler.c
 + *  \ingroup spview3d
 + */
 +
 +#include "BLI_listbase.h"
 +#include "BLI_string.h"
 +#include "BLI_rect.h"
 +#include "BLI_math.h"
 +#include "BLI_utildefines.h"
 +
 +#include "BLT_translation.h"
 +
 +#include "BKE_context.h"
-                       CTX_data_scene(C), CTX_data_depsgraph(C), 0,
 +#include "BKE_gpencil.h"
++#include "BKE_main.h"
++
++#include "BKE_object.h"
 +#include "BKE_unit.h"
 +
 +#include "DNA_object_types.h"
 +#include "DNA_gpencil_types.h"
 +#include "DNA_view3d_types.h"
 +
 +#include "BIF_gl.h"
 +
 +#include "ED_screen.h"
 +#include "ED_transform_snap_object_context.h"
 +#include "ED_view3d.h"
 +
 +#include "UI_resources.h"
 +#include "UI_interface.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "RNA_access.h"
 +
 +#include "WM_api.h"
 +#include "WM_types.h"
 +#include "WM_toolsystem.h"
 +
 +#include "view3d_intern.h"  /* own include */
 +
 +#include "GPU_immediate.h"
 +#include "GPU_immediate_util.h"
 +#include "GPU_select.h"
 +
 +#include "BLF_api.h"
 +
 +
 +static const char *view3d_wgt_ruler_id = "VIEW3D_WGT_ruler";
 +
 +
 +#define MVAL_MAX_PX_DIST 12.0f
 +
 +/* -------------------------------------------------------------------- */
 +/* Ruler Item (we can have many) */
 +enum {
 +      RULERITEM_USE_ANGLE = (1 << 0),  /* use protractor */
 +      RULERITEM_USE_RAYCAST = (1 << 1)
 +};
 +
 +enum {
 +      RULERITEM_DIRECTION_IN = 0,
 +      RULERITEM_DIRECTION_OUT
 +};
 +
 +/* keep smaller then selection, since we may want click elsewhere without selecting a ruler */
 +#define RULER_PICK_DIST 12.0f
 +#define RULER_PICK_DIST_SQ (RULER_PICK_DIST * RULER_PICK_DIST)
 +
 +/* not clicking on a point */
 +#define PART_LINE 0xff
 +
 +/* -------------------------------------------------------------------- */
 +/* Ruler Info (wmManipulatorGroup customdata) */
 +
 +enum {
 +      RULER_STATE_NORMAL = 0,
 +      RULER_STATE_DRAG
 +};
 +
 +enum {
 +      RULER_SNAP_OK = (1 << 0),
 +};
 +
 +typedef struct RulerInfo {
 +      // ListBase items;
 +      int      item_active;
 +      int flag;
 +      int snap_flag;
 +      int state;
 +
 +      struct SnapObjectContext *snap_context;
 +
 +      /* wm state */
 +      wmWindow *win;
 +      ScrArea *sa;
 +      ARegion *ar;  /* re-assigned every modal update */
 +} RulerInfo;
 +
 +/* -------------------------------------------------------------------- */
 +/* Ruler Item (two or three points) */
 +
 +typedef struct RulerItem {
 +      wmManipulator mpr;
 +
 +      /* worldspace coords, middle being optional */
 +      float co[3][3];
 +
 +      int   flag;
 +      int   raycast_dir;  /* RULER_DIRECTION_* */
 +} RulerItem;
 +
 +typedef struct RulerInteraction {
 +      /* selected coord */
 +      char  co_index; /* 0 -> 2 */
 +      float drag_start_co[3];
 +      uint inside_region : 1;
 +} RulerInteraction;
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Internal Ruler Utilities
 + * \{ */
 +
 +static RulerItem *ruler_item_add(wmManipulatorGroup *mgroup)
 +{
 +      /* could pass this as an arg */
 +      const wmManipulatorType *wt_ruler = WM_manipulatortype_find("VIEW3D_WT_ruler_item", true);
 +      RulerItem *ruler_item = (RulerItem *)WM_manipulator_new_ptr(wt_ruler, mgroup, NULL);
 +      WM_manipulator_set_flag(&ruler_item->mpr, WM_MANIPULATOR_DRAW_MODAL, true);
 +      return ruler_item;
 +}
 +
 +static void ruler_item_remove(bContext *C, wmManipulatorGroup *mgroup, RulerItem *ruler_item)
 +{
 +      WM_manipulator_unlink(&mgroup->manipulators, mgroup->parent_mmap, &ruler_item->mpr, C);
 +}
 +
 +static void ruler_item_as_string(RulerItem *ruler_item, UnitSettings *unit,
 +                                 char *numstr, size_t numstr_size, int prec)
 +{
 +      const bool do_split = (unit->flag & USER_UNIT_OPT_SPLIT) != 0;
 +
 +      if (ruler_item->flag & RULERITEM_USE_ANGLE) {
 +              const float ruler_angle = angle_v3v3v3(ruler_item->co[0],
 +                                                     ruler_item->co[1],
 +                                                     ruler_item->co[2]);
 +
 +              if (unit->system == USER_UNIT_NONE) {
 +                      BLI_snprintf(numstr, numstr_size, "%.*f°", prec, RAD2DEGF(ruler_angle));
 +              }
 +              else {
 +                      bUnit_AsString(numstr, numstr_size,
 +                                     (double)ruler_angle,
 +                                     prec, unit->system, B_UNIT_ROTATION, do_split, false);
 +              }
 +      }
 +      else {
 +              const float ruler_len = len_v3v3(ruler_item->co[0],
 +                                               ruler_item->co[2]);
 +
 +              if (unit->system == USER_UNIT_NONE) {
 +                      BLI_snprintf(numstr, numstr_size, "%.*f", prec, ruler_len);
 +              }
 +              else {
 +                      bUnit_AsString(numstr, numstr_size,
 +                                     (double)(ruler_len * unit->scale_length),
 +                                     prec, unit->system, B_UNIT_LENGTH, do_split, false);
 +              }
 +      }
 +}
 +
 +static bool view3d_ruler_pick(
 +        wmManipulatorGroup *mgroup, RulerItem *ruler_item, const float mval[2],
 +        int *r_co_index)
 +{
 +      RulerInfo *ruler_info = mgroup->customdata;
 +      ARegion *ar = ruler_info->ar;
 +      bool found = false;
 +
 +      float dist_best = RULER_PICK_DIST_SQ;
 +      int co_index_best = -1;
 +
 +      {
 +              float co_ss[3][2];
 +              float dist;
 +              int j;
 +
 +              /* should these be checked? - ok for now not to */
 +              for (j = 0; j < 3; j++) {
 +                      ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
 +              }
 +
 +              if (ruler_item->flag & RULERITEM_USE_ANGLE) {
 +                      dist = min_ff(dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[1]),
 +                                    dist_squared_to_line_segment_v2(mval, co_ss[1], co_ss[2]));
 +                      if (dist < dist_best) {
 +                              dist_best = dist;
 +                              found = true;
 +
 +                              {
 +                                      const float dist_points[3] = {
 +                                          len_squared_v2v2(co_ss[0], mval),
 +                                          len_squared_v2v2(co_ss[1], mval),
 +                                          len_squared_v2v2(co_ss[2], mval),
 +                                      };
 +                                      if (min_fff(UNPACK3(dist_points)) < RULER_PICK_DIST_SQ) {
 +                                              co_index_best = min_axis_v3(dist_points);
 +                                      }
 +                                      else {
 +                                              co_index_best = -1;
 +                                      }
 +                              }
 +                      }
 +              }
 +              else {
 +                      dist = dist_squared_to_line_segment_v2(mval, co_ss[0], co_ss[2]);
 +                      if (dist < dist_best) {
 +                              dist_best = dist;
 +                              found = true;
 +
 +                              {
 +                                      const float dist_points[2] = {
 +                                          len_squared_v2v2(co_ss[0], mval),
 +                                          len_squared_v2v2(co_ss[2], mval),
 +                                      };
 +                                      if (min_ff(UNPACK2(dist_points)) < RULER_PICK_DIST_SQ) {
 +                                              co_index_best = (dist_points[0] < dist_points[1]) ? 0 : 2;
 +                                      }
 +                                      else {
 +                                              co_index_best = -1;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      *r_co_index = co_index_best;
 +      return found;
 +}
 +
 +/**
 + * Ensure the 'snap_context' is only cached while dragging,
 + * needed since the user may toggle modes between tool use.
 + */
 +static void ruler_state_set(bContext *C, RulerInfo *ruler_info, int state)
 +{
++      Main *bmain = CTX_data_main(C);
 +      if (state == ruler_info->state) {
 +              return;
 +      }
 +
 +      /* always remove */
 +      if (ruler_info->snap_context) {
 +              ED_transform_snap_object_context_destroy(ruler_info->snap_context);
 +              ruler_info->snap_context = NULL;
 +      }
 +
 +      if (state == RULER_STATE_NORMAL) {
 +              /* pass */
 +      }
 +      else if (state == RULER_STATE_DRAG) {
 +              ruler_info->snap_context = ED_transform_snap_object_context_create_view3d(
++                      bmain, CTX_data_scene(C), CTX_data_depsgraph(C), 0,
 +                      ruler_info->ar, CTX_wm_view3d(C));
 +      }
 +      else {
 +              BLI_assert(0);
 +      }
 +
 +      ruler_info->state = state;
 +}
 +
 +static void view3d_ruler_item_project(
 +        RulerInfo *ruler_info, float r_co[3],
 +        const int xy[2])
 +{
 +      ED_view3d_win_to_3d_int(ruler_info->sa->spacedata.first, ruler_info->ar, r_co, xy, r_co);
 +}
 +
 +/* use for mousemove events */
 +static bool view3d_ruler_item_mousemove(
 +        RulerInfo *ruler_info, RulerItem *ruler_item, const int mval[2],
 +        const bool do_thickness, const bool do_snap)
 +{
 +      RulerInteraction *inter = ruler_item->mpr.interaction_data;
 +      const float eps_bias = 0.0002f;
 +      float dist_px = MVAL_MAX_PX_DIST * U.pixelsize;  /* snap dist */
 +
 +      ruler_info->snap_flag &= ~RULER_SNAP_OK;
 +
 +      if (ruler_item) {
 +              float *co = ruler_item->co[inter->co_index];
 +              /* restore the initial depth */
 +              copy_v3_v3(co, inter->drag_start_co);
 +              view3d_ruler_item_project(ruler_info, co, mval);
 +              if (do_thickness && inter->co_index != 1) {
 +                      // Scene *scene = CTX_data_scene(C);
 +                      // View3D *v3d = ruler_info->sa->spacedata.first;
 +                      const float mval_fl[2] = {UNPACK2(mval)};
 +                      float ray_normal[3];
 +                      float ray_start[3];
 +                      float *co_other;
 +
 +                      co_other = ruler_item->co[inter->co_index == 0 ? 2 : 0];
 +
 +                      if (ED_transform_snap_object_project_view3d(
 +                              ruler_info->snap_context,
 +                              SCE_SNAP_MODE_FACE,
 +                              &(const struct SnapObjectParams){
 +                                  .snap_select = SNAP_ALL,
 +                                  .use_object_edit_cage = true,
 +                              },
 +                              mval_fl, &dist_px,
 +                              co, ray_normal))
 +                      {
 +                              negate_v3(ray_normal);
 +                              /* add some bias */
 +                              madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias);
 +                              ED_transform_snap_object_project_ray(
 +                                      ruler_info->snap_context,
 +                                      &(const struct SnapObjectParams){
 +                                          .snap_select = SNAP_ALL,
 +                                          .use_object_edit_cage = true,
 +                                      },
 +                                      ray_start, ray_normal, NULL,
 +                                      co_other, NULL);
 +                      }
 +              }
 +              else if (do_snap) {
 +                      const float mval_fl[2] = {UNPACK2(mval)};
 +
 +                      if (ED_transform_snap_object_project_view3d(
 +                              ruler_info->snap_context,
 +                              (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
 +                              &(const struct SnapObjectParams){
 +                                  .snap_select = SNAP_ALL,
 +                                  .use_object_edit_cage = true,
 +                                  .use_occlusion_test = true,
 +                              },
 +                              mval_fl, &dist_px,
 +                              co, NULL))
 +                      {
 +                              ruler_info->snap_flag |= RULER_SNAP_OK;
 +                      }
 +              }
 +              return true;
 +      }
 +      else {
 +              return false;
 +      }
 +}
 +
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Ruler/Grease Pencil Conversion
 + * \{ */
 +
 +#define RULER_ID "RulerData3D"
 +static bool view3d_ruler_to_gpencil(bContext *C, wmManipulatorGroup *mgroup)
 +{
 +      // RulerInfo *ruler_info = mgroup->customdata;
 +      Scene *scene = CTX_data_scene(C);
 +      bGPDlayer *gpl;
 +      bGPDframe *gpf;
 +      bGPDstroke *gps;
 +      bGPDpalette *palette;
 +      bGPDpalettecolor *palcolor;
 +      RulerItem *ruler_item;
 +      const char *ruler_name = RULER_ID;
 +      bool changed = false;
 +
 +      if (scene->gpd == NULL) {
 +              scene->gpd = BKE_gpencil_data_addnew("GPencil");
 +      }
 +
 +      gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
 +      if (gpl == NULL) {
 +              gpl = BKE_gpencil_layer_addnew(scene->gpd, ruler_name, false);
 +              gpl->thickness = 1;
 +              gpl->flag |= GP_LAYER_HIDE;
 +      }
 +
 +      /* try to get active palette or create a new one */
 +      palette = BKE_gpencil_palette_getactive(scene->gpd);
 +      if (palette == NULL) {
 +              palette = BKE_gpencil_palette_addnew(scene->gpd, DATA_("GP_Palette"), true);
 +      }
 +      /* try to get color with the ruler name or create a new one */
 +      palcolor = BKE_gpencil_palettecolor_getbyname(palette, (char *)ruler_name);
 +      if (palcolor == NULL) {
 +              palcolor = BKE_gpencil_palettecolor_addnew(palette, (char *)ruler_name, true);
 +      }
 +
 +      gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true);
 +      BKE_gpencil_free_strokes(gpf);
 +
 +      for (ruler_item = mgroup->manipulators.first; ruler_item; ruler_item = (RulerItem *)ruler_item->mpr.next) {
 +              bGPDspoint *pt;
 +              int j;
 +
 +              /* allocate memory for a new stroke */
 +              gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
 +              if (ruler_item->flag & RULERITEM_USE_ANGLE) {
 +                      gps->totpoints = 3;
 +                      pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
 +                      for (j = 0; j < 3; j++) {
 +                              copy_v3_v3(&pt->x, ruler_item->co[j]);
 +                              pt->pressure = 1.0f;
 +                              pt->strength = 1.0f;
 +                              pt++;
 +                      }
 +              }
 +              else {
 +                      gps->totpoints = 2;
 +                      pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
 +                      for (j = 0; j < 3; j += 2) {
 +                              copy_v3_v3(&pt->x, ruler_item->co[j]);
 +                              pt->pressure = 1.0f;
 +                              pt->strength = 1.0f;
 +                              pt++;
 +                      }
 +              }
 +              gps->flag = GP_STROKE_3DSPACE;
 +              gps->thickness = 3;
 +              /* assign color to stroke */
 +              BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname));
 +              gps->palcolor = palcolor;
 +              BLI_addtail(&gpf->strokes, gps);
 +              changed = true;
 +      }
 +
 +      return changed;
 +}
 +
 +static bool view3d_ruler_from_gpencil(const bContext *C, wmManipulatorGroup *mgroup)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      bool changed = false;
 +
 +      if (scene->gpd) {
 +              bGPDlayer *gpl;
 +              const char *ruler_name = RULER_ID;
 +              gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info));
 +              if (gpl) {
 +                      bGPDframe *gpf;
 +                      gpf = BKE_gpencil_layer_getframe(gpl, CFRA, false);
 +                      if (gpf) {
 +                              bGPDstroke *gps;
 +                              for (gps = gpf->strokes.first; gps; gps = gps->next) {
 +                                      bGPDspoint *pt = gps->points;
 +                                      int j;
 +                                      RulerItem *ruler_item = NULL;
 +                                      if (gps->totpoints == 3) {
 +                                              ruler_item = ruler_item_add(mgroup);
 +                                              for (j = 0; j < 3; j++) {
 +                                                      copy_v3_v3(ruler_item->co[j], &pt->x);
 +                                                      pt++;
 +                                              }
 +                                              ruler_item->flag |= RULERITEM_USE_ANGLE;
 +                                              changed = true;
 +                                      }
 +                                      else if (gps->totpoints == 2) {
 +                                              ruler_item = ruler_item_add(mgroup);
 +                                              for (j = 0; j < 3; j += 2) {
 +                                                      copy_v3_v3(ruler_item->co[j], &pt->x);
 +                                                      pt++;
 +                                              }
 +                                              changed = true;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      return changed;
 +}
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Ruler Item Manipulator Type
 + * \{ */
 +
 +static void manipulator_ruler_draw(const bContext *C, wmManipulator *mpr)
 +{
 +      Scene *scene = CTX_data_scene(C);
 +      UnitSettings *unit = &scene->unit;
 +      RulerInfo *ruler_info = mpr->parent_mgroup->customdata;
 +      RulerItem *ruler_item = (RulerItem *)mpr;
 +      ARegion *ar = ruler_info->ar;
 +      RegionView3D *rv3d = ar->regiondata;
 +      const float cap_size = 4.0f;
 +      const float bg_margin = 4.0f * U.pixelsize;
 +      const float bg_radius = 4.0f * U.pixelsize;
 +      const float arc_size = 64.0f * U.pixelsize;
 +#define ARC_STEPS 24
 +      const int arc_steps = ARC_STEPS;
 +      const float color_act[4] = {1.0f, 1.0f, 1.0f, 1.0f};
 +      const float color_base[4] = {0.0f, 0.0f, 0.0f, 1.0f};
 +      unsigned char color_text[3];
 +      unsigned char color_wire[3];
 +      float color_back[4] = {1.0f, 1.0f, 1.0f, 0.5f};
 +
 +      /* anti-aliased lines for more consistent appearance */
 +      glEnable(GL_LINE_SMOOTH);
 +
 +      BLF_enable(blf_mono_font, BLF_ROTATION);
 +      BLF_size(blf_mono_font, 14 * U.pixelsize, U.dpi);
 +      BLF_rotation(blf_mono_font, 0.0f);
 +
 +      UI_GetThemeColor3ubv(TH_TEXT, color_text);
 +      UI_GetThemeColor3ubv(TH_WIRE, color_wire);
 +
 +      const bool is_act = (mpr->flag & WM_MANIPULATOR_DRAW_HOVER);
 +      float dir_ruler[2];
 +      float co_ss[3][2];
 +      int j;
 +
 +      /* should these be checked? - ok for now not to */
 +      for (j = 0; j < 3; j++) {
 +              ED_view3d_project_float_global(ar, ruler_item->co[j], co_ss[j], V3D_PROJ_TEST_NOP);
 +      }
 +
 +      glEnable(GL_BLEND);
 +
 +      const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +
 +      if (ruler_item->flag & RULERITEM_USE_ANGLE) {
 +              immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
 +
 +              float viewport_size[4];
 +              glGetFloatv(GL_VIEWPORT, viewport_size);
 +              immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
 +
 +              immUniform1i("num_colors", 2);  /* "advanced" mode */
 +              const float *col = is_act ? color_act : color_base;
 +              immUniformArray4fv("colors", (float *)(float[][4]){{0.67f, 0.67f, 0.67f, 1.0f}, {col[0], col[1], col[2], col[3]}}, 2);
 +              immUniform1f("dash_width", 6.0f);
 +
 +              immBegin(GWN_PRIM_LINE_STRIP, 3);
 +
 +              immVertex2fv(shdr_pos, co_ss[0]);
 +              immVertex2fv(shdr_pos, co_ss[1]);
 +              immVertex2fv(shdr_pos, co_ss[2]);
 +
 +              immEnd();
 +
 +              immUnbindProgram();
 +
 +              immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +
 +              /* arc */
 +              {
 +                      float dir_tmp[3];
 +                      float co_tmp[3];
 +                      float arc_ss_coord[2];
 +
 +                      float dir_a[3];
 +                      float dir_b[3];
 +                      float quat[4];
 +                      float axis[3];
 +                      float angle;
 +                      const float px_scale = (ED_view3d_pixel_size(rv3d, ruler_item->co[1]) *
 +                                              min_fff(arc_size,
 +                                                      len_v2v2(co_ss[0], co_ss[1]) / 2.0f,
 +                                                      len_v2v2(co_ss[2], co_ss[1]) / 2.0f));
 +
 +                      sub_v3_v3v3(dir_a, ruler_item->co[0], ruler_item->co[1]);
 +                      sub_v3_v3v3(dir_b, ruler_item->co[2], ruler_item->co[1]);
 +                      normalize_v3(dir_a);
 +                      normalize_v3(dir_b);
 +
 +                      cross_v3_v3v3(axis, dir_a, dir_b);
 +                      angle = angle_normalized_v3v3(dir_a, dir_b);
 +
 +                      axis_angle_to_quat(quat, axis, angle / arc_steps);
 +
 +                      copy_v3_v3(dir_tmp, dir_a);
 +
 +                      immUniformColor3ubv(color_wire);
 +
 +                      immBegin(GWN_PRIM_LINE_STRIP, arc_steps + 1);
 +
 +                      for (j = 0; j <= arc_steps; j++) {
 +                              madd_v3_v3v3fl(co_tmp, ruler_item->co[1], dir_tmp, px_scale);
 +                              ED_view3d_project_float_global(ar, co_tmp, arc_ss_coord, V3D_PROJ_TEST_NOP);
 +                              mul_qt_v3(quat, dir_tmp);
 +
 +                              immVertex2fv(shdr_pos, arc_ss_coord);
 +                      }
 +
 +                      immEnd();
 +              }
 +
 +              /* capping */
 +              {
 +                      float rot_90_vec_a[2];
 +                      float rot_90_vec_b[2];
 +                      float cap[2];
 +
 +                      sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[1]);
 +                      rot_90_vec_a[0] = -dir_ruler[1];
 +                      rot_90_vec_a[1] =  dir_ruler[0];
 +                      normalize_v2(rot_90_vec_a);
 +
 +                      sub_v2_v2v2(dir_ruler, co_ss[1], co_ss[2]);
 +                      rot_90_vec_b[0] = -dir_ruler[1];
 +                      rot_90_vec_b[1] =  dir_ruler[0];
 +                      normalize_v2(rot_90_vec_b);
 +
 +                      glEnable(GL_BLEND);
 +
 +                      immUniformColor3ubv(color_wire);
 +
 +                      immBegin(GWN_PRIM_LINES, 8);
 +
 +                      madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, cap_size);
 +                      immVertex2fv(shdr_pos, cap);
 +                      madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec_a, -cap_size);
 +                      immVertex2fv(shdr_pos, cap);
 +
 +                      madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, cap_size);
 +                      immVertex2fv(shdr_pos, cap);
 +                      madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec_b, -cap_size);
 +                      immVertex2fv(shdr_pos, cap);
 +
 +                      /* angle vertex */
 +                      immVertex2f(shdr_pos, co_ss[1][0] - cap_size, co_ss[1][1] - cap_size);
 +                      immVertex2f(shdr_pos, co_ss[1][0] + cap_size, co_ss[1][1] + cap_size);
 +                      immVertex2f(shdr_pos, co_ss[1][0] - cap_size, co_ss[1][1] + cap_size);
 +                      immVertex2f(shdr_pos, co_ss[1][0] + cap_size, co_ss[1][1] - cap_size);
 +
 +                      immEnd();
 +
 +                      glDisable(GL_BLEND);
 +              }
 +
 +              immUnbindProgram();
 +
 +              /* text */
 +              {
 +                      char numstr[256];
 +                      float numstr_size[2];
 +                      float posit[2];
 +                      const int prec = 2;  /* XXX, todo, make optional */
 +
 +                      ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
 +
 +                      BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
 +
 +                      posit[0] = co_ss[1][0] + (cap_size * 2.0f);
 +                      posit[1] = co_ss[1][1] - (numstr_size[1] / 2.0f);
 +
 +                      /* draw text (bg) */
 +                      UI_draw_roundbox_corner_set(UI_CNR_ALL);
 +                      UI_draw_roundbox_aa(
 +                              true,
 +                              posit[0] - bg_margin,                  posit[1] - bg_margin,
 +                              posit[0] + bg_margin + numstr_size[0], posit[1] + bg_margin + numstr_size[1],
 +                              bg_radius, color_back);
 +                      /* draw text */
 +                      BLF_color3ubv(blf_mono_font, color_text);
 +                      BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
 +                      BLF_rotation(blf_mono_font, 0.0f);
 +                      BLF_draw(blf_mono_font, numstr, sizeof(numstr));
 +              }
 +      }
 +      else {
 +              immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
 +
 +              float viewport_size[4];
 +              glGetFloatv(GL_VIEWPORT, viewport_size);
 +              immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
 +
 +              immUniform1i("num_colors", 2);  /* "advanced" mode */
 +              const float *col = is_act ? color_act : color_base;
 +              immUniformArray4fv("colors", (float *)(float[][4]){{0.67f, 0.67f, 0.67f, 1.0f}, {col[0], col[1], col[2], col[3]}}, 2);
 +              immUniform1f("dash_width", 6.0f);
 +
 +              immBegin(GWN_PRIM_LINES, 2);
 +
 +              immVertex2fv(shdr_pos, co_ss[0]);
 +              immVertex2fv(shdr_pos, co_ss[2]);
 +
 +              immEnd();
 +
 +              immUnbindProgram();
 +
 +              immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +
 +              sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]);
 +
 +              /* capping */
 +              {
 +                      float rot_90_vec[2] = {-dir_ruler[1], dir_ruler[0]};
 +                      float cap[2];
 +
 +                      normalize_v2(rot_90_vec);
 +
 +                      glEnable(GL_BLEND);
 +
 +                      immUniformColor3ubv(color_wire);
 +
 +                      immBegin(GWN_PRIM_LINES, 4);
 +
 +                      madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, cap_size);
 +                      immVertex2fv(shdr_pos, cap);
 +                      madd_v2_v2v2fl(cap, co_ss[0], rot_90_vec, -cap_size);
 +                      immVertex2fv(shdr_pos, cap);
 +
 +                      madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, cap_size);
 +                      immVertex2fv(shdr_pos, cap);
 +                      madd_v2_v2v2fl(cap, co_ss[2], rot_90_vec, -cap_size);
 +                      immVertex2fv(shdr_pos, cap);
 +
 +                      immEnd();
 +
 +                      glDisable(GL_BLEND);
 +              }
 +
 +              immUnbindProgram();
 +
 +              /* text */
 +              {
 +                      char numstr[256];
 +                      float numstr_size[2];
 +                      const int prec = 6;  /* XXX, todo, make optional */
 +                      float posit[2];
 +
 +                      ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
 +
 +                      BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
 +
 +                      mid_v2_v2v2(posit, co_ss[0], co_ss[2]);
 +
 +                      /* center text */
 +                      posit[0] -= numstr_size[0] / 2.0f;
 +                      posit[1] -= numstr_size[1] / 2.0f;
 +
 +                      /* draw text (bg) */
 +                      UI_draw_roundbox_corner_set(UI_CNR_ALL);
 +                      UI_draw_roundbox_aa(
 +                              true,
 +                              posit[0] - bg_margin,                  posit[1] - bg_margin,
 +                              posit[0] + bg_margin + numstr_size[0], posit[1] + bg_margin + numstr_size[1],
 +                              bg_radius, color_back);
 +                      /* draw text */
 +                      BLF_color3ubv(blf_mono_font, color_text);
 +                      BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
 +                      BLF_draw(blf_mono_font, numstr, sizeof(numstr));
 +              }
 +      }
 +
 +      glDisable(GL_LINE_SMOOTH);
 +
 +      BLF_disable(blf_mono_font, BLF_ROTATION);
 +
 +#undef ARC_STEPS
 +
 +      /* draw snap */
 +      if ((ruler_info->snap_flag & RULER_SNAP_OK) &&
 +          (ruler_info->state == RULER_STATE_DRAG) &&
 +          (ruler_item->mpr.interaction_data != NULL))
 +      {
 +              RulerInteraction *inter = ruler_item->mpr.interaction_data;
 +              /* size from drawSnapping */
 +              const float size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
 +              float co_ss_snap[3];
 +              ED_view3d_project_float_global(ar, ruler_item->co[inter->co_index], co_ss_snap, V3D_PROJ_TEST_NOP);
 +
 +              unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
 +
 +              immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
 +              immUniformColor4fv(color_act);
 +
 +              imm_draw_circle_wire_2d(pos, co_ss_snap[0], co_ss_snap[1], size * U.pixelsize, 32);
 +
 +              immUnbindProgram();
 +      }
 +}
 +
 +static int manipulator_ruler_test_select(
 +        bContext *UNUSED(C), wmManipulator *mpr, const wmEvent *event)
 +{
 +      RulerItem *ruler_item_pick = (RulerItem *)mpr;
 +      float mval_fl[2] = {UNPACK2(event->mval)};
 +      int co_index;
 +
 +      /* select and drag */
 +      if (view3d_ruler_pick(mpr->parent_mgroup, ruler_item_pick, mval_fl, &co_index)) {
 +              if (co_index == -1) {
 +                      if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
 +                              return PART_LINE;
 +                      }
 +              }
 +              else {
 +                      return co_index;
 +              }
 +      }
 +      return -1;
 +}
 +
 +static int manipulator_ruler_modal(
 +        bContext *C, wmManipulator *mpr, const wmEvent *event,
 +        eWM_ManipulatorTweak UNUSED(tweak_flag))
 +{
 +      bool do_draw = false;
 +      int exit_code = OPERATOR_RUNNING_MODAL;
 +      RulerInfo *ruler_info = mpr->parent_mgroup->customdata;
 +      RulerItem *ruler_item = (RulerItem *)mpr;
 +      RulerInteraction *inter = ruler_item->mpr.interaction_data;
 +      ARegion *ar = CTX_wm_region(C);
 +
 +      ruler_info->ar = ar;
 +
 +      switch (event->type) {
 +              case MOUSEMOVE:
 +              {
 +                      if (ruler_info->state == RULER_STATE_DRAG) {
 +                              if (view3d_ruler_item_mousemove(
 +                                      ruler_info, ruler_item, event->mval,
 +                                      event->shift != 0, event->ctrl != 0))
 +                              {
 +                                      do_draw = true;
 +                              }
 +                              inter->inside_region = BLI_rcti_isect_pt_v(&ar->winrct, &event->x);
 +                      }
 +                      break;
 +              }
 +      }
 +      if (do_draw) {
 +              ED_region_tag_redraw(ar);
 +      }
 +      return exit_code;
 +}
 +
 +static int manipulator_ruler_invoke(
 +        bContext *C, wmManipulator *mpr, const wmEvent *event)
 +{
 +      wmManipulatorGroup *mgroup = mpr->parent_mgroup;
 +      RulerInfo *ruler_info = mgroup->customdata;
 +      RulerItem *ruler_item_pick = (RulerItem *)mpr;
 +      RulerInteraction *inter = MEM_callocN(sizeof(RulerInteraction), __func__);
 +      mpr->interaction_data = inter;
 +
 +      ARegion *ar = ruler_info->ar;
 +
 +      const float mval_fl[2] = {UNPACK2(event->mval)};
 +
 +      /* select and drag */
 +      if (mpr->highlight_part == PART_LINE) {
 +              if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
 +                      /* Add Center Point */
 +                      ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
 +                      inter->co_index = 1;
 +                      ruler_state_set(C, ruler_info, RULER_STATE_DRAG);
 +
 +                      /* find the factor */
 +                      {
 +                              float co_ss[2][2];
 +                              float fac;
 +
 +                              ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
 +                              ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);
 +
 +                              fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
 +                              CLAMP(fac, 0.0f, 1.0f);
 +
 +                              interp_v3_v3v3(ruler_item_pick->co[1],
 +                                             ruler_item_pick->co[0],
 +                                             ruler_item_pick->co[2], fac);
 +                      }
 +
 +                      /* update the new location */
 +                      view3d_ruler_item_mousemove(
 +                              ruler_info, ruler_item_pick, event->mval,
 +                              event->shift != 0, event->ctrl != 0);
 +              }
 +      }
 +      else {
 +              inter->co_index = mpr->highlight_part;
 +              ruler_state_set(C, ruler_info, RULER_STATE_DRAG);
 +
 +              /* store the initial depth */
 +              copy_v3_v3(inter->drag_start_co, ruler_item_pick->co[inter->co_index]);
 +      }
 +
 +      return OPERATOR_RUNNING_MODAL;
 +}
 +
 +static void manipulator_ruler_exit(bContext *C, wmManipulator *mpr, const bool cancel)
 +{
 +      wmManipulatorGroup *mgroup = mpr->parent_mgroup;
 +      RulerInfo *ruler_info = mgroup->customdata;
 +
 +      if (!cancel) {
 +              if (ruler_info->state == RULER_STATE_DRAG) {
 +                      RulerItem *ruler_item = (RulerItem *)mpr;
 +                      RulerInteraction *inter = mpr->interaction_data;
 +                      /* rubber-band angle removal */
 +                      if (!inter->inside_region) {
 +                              if ((inter->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) {
 +                                      ruler_item->flag &= ~RULERITEM_USE_ANGLE;
 +                              }
 +                              else {
 +                                      /* Not ideal, since the ruler isn't a mode and we don't want to override delete key
 +                                       * use dragging out of the view for removal. */
 +                                      ruler_item_remove(C, mgroup, ruler_item);
 +                                      ruler_item = NULL;
 +                                      mpr = NULL;
 +                                      inter = NULL;
 +                              }
 +                      }
 +                      if (ruler_info->snap_flag & RULER_SNAP_OK) {
 +                              ruler_info->snap_flag &= ~RULER_SNAP_OK;
 +                      }
 +                      ruler_state_set(C, ruler_info, RULER_STATE_NORMAL);
 +              }
 +              /* We could convert only the current manipulator, for now just re-generate. */
 +              view3d_ruler_to_gpencil(C, mgroup);
 +      }
 +
 +      if (mpr) {
 +              MEM_SAFE_FREE(mpr->interaction_data);
 +      }
 +
 +      ruler_state_set(C, ruler_info, RULER_STATE_NORMAL);
 +}
 +
 +static int manipulator_ruler_cursor_get(wmManipulator *mpr)
 +{
 +      if (mpr->highlight_part == PART_LINE) {
 +              return BC_CROSSCURSOR;
 +      }
 +      return BC_NSEW_SCROLLCURSOR;
 +}
 +
 +void VIEW3D_WT_ruler_item(wmManipulatorType *wt)
 +{
 +      /* identifiers */
 +      wt->idname = "VIEW3D_WT_ruler_item";
 +
 +      /* api callbacks */
 +      wt->draw = manipulator_ruler_draw;
 +      wt->test_select = manipulator_ruler_test_select;
 +      wt->modal = manipulator_ruler_modal;
 +      wt->invoke = manipulator_ruler_invoke;
 +      wt->exit = manipulator_ruler_exit;
 +      wt->cursor_get = manipulator_ruler_cursor_get;
 +
 +      wt->struct_size = sizeof(RulerItem);
 +}
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Ruler Manipulator Group
 + * \{ */
 +
 +static bool WIDGETGROUP_ruler_poll(const bContext *C, wmManipulatorGroupType *wgt)
 +{
 +      bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
 +      if ((tref_rt == NULL) ||
 +          !STREQ(wgt->idname, tref_rt->manipulator_group))
 +      {
 +              WM_manipulator_group_type_unlink_delayed_ptr(wgt);
 +              return false;
 +      }
 +      return true;
 +}
 +
 +static void WIDGETGROUP_ruler_setup(const bContext *C, wmManipulatorGroup *mgroup)
 +{
 +      RulerInfo *ruler_info = MEM_callocN(sizeof(RulerInfo), __func__);
 +
 +      if (view3d_ruler_from_gpencil(C, mgroup)) {
 +              /* nop */
 +      }
 +
 +      wmWindow *win = CTX_wm_window(C);
 +      ScrArea *sa = CTX_wm_area(C);
 +      ARegion *ar = CTX_wm_region(C);
 +      ruler_info->win = win;
 +      ruler_info->sa = sa;
 +      ruler_info->ar = ar;
 +
 +      mgroup->customdata = ruler_info;
 +}
 +
 +void VIEW3D_WGT_ruler(wmManipulatorGroupType *wgt)
 +{
 +      wgt->name = "Ruler Widgets";
 +      wgt->idname = view3d_wgt_ruler_id;
 +
 +      wgt->flag |= WM_MANIPULATORGROUPTYPE_SCALE | WM_MANIPULATORGROUPTYPE_DRAW_MODAL_ALL;
 +
 +      wgt->mmap_params.spaceid = SPACE_VIEW3D;
 +      wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
 +
 +      wgt->poll = WIDGETGROUP_ruler_poll;
 +      wgt->setup = WIDGETGROUP_ruler_setup;
 +}
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Add Ruler Operator
 + * \{ */
 +
 +static int view3d_ruler_poll(bContext *C)
 +{
 +      bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
 +      if ((tref_rt == NULL) ||
 +          !STREQ(view3d_wgt_ruler_id, tref_rt->manipulator_group) ||
 +          CTX_wm_region_view3d(C) == NULL)
 +      {
 +              return false;
 +      }
 +      return true;
 +}
 +
 +static int view3d_ruler_add_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
 +{
 +      ARegion *ar = CTX_wm_region(C);
 +      View3D *v3d = CTX_wm_view3d(C);
 +      RegionView3D *rv3d = ar->regiondata;
 +
 +      wmManipulatorMap *mmap = ar->manipulator_map;
 +      wmManipulatorGroup *mgroup = WM_manipulatormap_group_find(mmap, view3d_wgt_ruler_id);
 +      const bool use_depth = (v3d->drawtype >= OB_SOLID);
 +
 +      /* Create new line */
 +      RulerItem *ruler_item;
 +      ruler_item = ruler_item_add(mgroup);
 +
 +      /* This is a little weak, but there is no real good way to tweak directly. */
 +      WM_manipulator_highlight_set(mmap, &ruler_item->mpr);
 +      if (WM_operator_name_call(
 +              C, "MANIPULATORGROUP_OT_manipulator_tweak",
 +              WM_OP_INVOKE_REGION_WIN, NULL) == OPERATOR_RUNNING_MODAL)
 +      {
 +              RulerInfo *ruler_info = mgroup->customdata;
 +              RulerInteraction *inter = ruler_item->mpr.interaction_data;
 +              if (use_depth) {
 +                      /* snap the first point added, not essential but handy */
 +                      inter->co_index = 0;
 +                      view3d_ruler_item_mousemove(ruler_info, ruler_item, event->mval, false, true);
 +                      copy_v3_v3(inter->drag_start_co, ruler_item->co[inter->co_index]);
 +              }
 +              else {
 +                      negate_v3_v3(inter->drag_start_co, rv3d->ofs);
 +                      copy_v3_v3(ruler_item->co[0], inter->drag_start_co);
 +                      view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval);
 +              }
 +
 +              copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
 +              ruler_item->mpr.highlight_part = inter->co_index = 2;
 +      }
 +      return OPERATOR_FINISHED;
 +}
 +
 +void VIEW3D_OT_ruler_add(wmOperatorType *ot)
 +{
 +      /* identifiers */
 +      ot->name = "Ruler Add";
 +      ot->idname = "VIEW3D_OT_ruler_add";
 +      ot->description = "";
 +
 +      ot->invoke = view3d_ruler_add_invoke;
 +      ot->poll = view3d_ruler_poll;
 +
 +      /* flags */
 +      ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
 +}
 +
 +/** \} */
@@@ -38,8 -38,8 +38,9 @@@
  #include "BLT_translation.h"
  
  #include "BKE_context.h"
--#include "BKE_unit.h"
  #include "BKE_gpencil.h"
++#include "BKE_main.h"
++#include "BKE_unit.h"
  
  #include "BIF_gl.h"
  
@@@ -267,6 -264,6 +268,7 @@@ static bool view3d_ruler_pick(RulerInf
   */
  static void ruler_state_set(bContext *C, RulerInfo *ruler_info, int state)
  {
++      Main *bmain = CTX_data_main(C);
        if (state == ruler_info->state) {
                return;
        }
        }
        else if (state == RULER_STATE_DRAG) {
                ruler_info->snap_context = ED_transform_snap_object_context_create_view3d(
-                       CTX_data_scene(C), CTX_data_depsgraph(C), 0,
 -                      CTX_data_main(C), CTX_data_scene(C), 0,
++                      bmain, CTX_data_scene(C), CTX_data_depsgraph(C), 0,
                        ruler_info->ar, CTX_wm_view3d(C));
        }
        else {
@@@ -116,9 -109,8 +116,10 @@@ void ED_view3d_viewcontext_init(bContex
  {
        memset(vc, 0, sizeof(ViewContext));
        vc->ar = CTX_wm_region(C);
+       vc->bmain = CTX_data_main(C);
 +      vc->depsgraph = CTX_data_depsgraph(C);
        vc->scene = CTX_data_scene(C);
 +      vc->view_layer = CTX_data_view_layer(C);
        vc->v3d = CTX_wm_view3d(C);
        vc->win = CTX_wm_window(C);
        vc->rv3d = CTX_wm_region_view3d(C);
@@@ -46,6 -46,8 +46,7 @@@
  
  #include "BKE_camera.h"
  #include "BKE_context.h"
 -#include "BKE_depsgraph.h" /* for ED_view3d_camera_lock_sync */
+ #include "BKE_main.h"
  #include "BKE_object.h"
  #include "BKE_screen.h"
  
@@@ -899,7 -878,7 +900,7 @@@ static float view_autodist_depth_margin
   * \param fallback_depth_pt: Use this points depth when no depth can be found.
   */
  bool ED_view3d_autodist(
-         struct Depsgraph *depsgraph, ARegion *ar, View3D *v3d,
 -        Main *bmain, Scene *scene, ARegion *ar, View3D *v3d,
++        Depsgraph *depsgraph, ARegion *ar, View3D *v3d,
          const int mval[2], float mouse_worldloc[3],
          const bool alphaoverride, const float fallback_depth_pt[3])
  {
        }
  }
  
- void ED_view3d_autodist_init(struct Depsgraph *depsgraph,
 -void ED_view3d_autodist_init(Main *bmain, Scene *scene, ARegion *ar, View3D *v3d, int mode)
++void ED_view3d_autodist_init(Depsgraph *depsgraph,
 +        ARegion *ar, View3D *v3d, int mode)
  {
        /* Get Z Depths, needed for perspective, nice for ortho */
        switch (mode) {
@@@ -37,6 -37,6 +37,7 @@@
  #include "BLI_utildefines.h"
  
  #include "BKE_context.h"
++#include "BKE_main.h"
  #include "BKE_report.h"
  
  #include "BLT_translation.h"
@@@ -507,6 -493,6 +508,7 @@@ static float userdef_speed = -1.f
  
  static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op)
  {
++      Main *bmain = CTX_data_main(C);
        wmWindow *win = CTX_wm_window(C);
  
        walk->rv3d = CTX_wm_region_view3d(C);
        walk->rv3d->rflag |= RV3D_NAVIGATING;
  
        walk->snap_context = ED_transform_snap_object_context_create_view3d(
-               walk->scene, CTX_data_depsgraph(C), 0,
 -              CTX_data_main(C), walk->scene, 0,
++              bmain, walk->scene, CTX_data_depsgraph(C), 0,
                walk->ar, walk->v3d);
  
        walk->v3d_camera_control = ED_view3d_cameracontrol_acquire(
@@@ -870,25 -828,25 +872,25 @@@ void transform_autoik_update(TransInfo 
                }
        }
  
 -      /* sanity checks (don't assume t->poseobj is set, or that it is an armature) */
 -      if (ELEM(NULL, t->poseobj, t->poseobj->pose))
 -              return;
 -
        /* apply to all pose-channels */
        bool changed = false;
 -      for (pchan = t->poseobj->pose->chanbase.first; pchan; pchan = pchan->next) {
 -              changed |= pchan_autoik_adjust(pchan, *chainlen);
 -      }
  
 -#ifdef WITH_LEGACY_DEPSGRAPH
 -      if (!DEG_depsgraph_use_legacy())
 -#endif
 -      {
 -              if (changed) {
 -                      /* TODO(sergey): Consider doing partial update only. */
 -                      DAG_relations_tag_update(bmain);
 +      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +
 +              /* sanity checks (don't assume t->poseobj is set, or that it is an armature) */
 +              if (ELEM(NULL, tc->poseobj, tc->poseobj->pose)) {
 +                      continue;
 +              }
 +
 +              for (pchan = tc->poseobj->pose->chanbase.first; pchan; pchan = pchan->next) {
 +                      changed |= pchan_autoik_adjust(pchan, *chainlen);
                }
        }
-               DEG_relations_tag_update(G.main);
 +
 +      if (changed) {
 +              /* TODO(sergey): Consider doing partial update only. */
++              DEG_relations_tag_update(bmain);
 +      }
  }
  
  /* frees temporal IKs */
@@@ -928,9 -889,12 +930,9 @@@ static void pose_grab_with_ik_clear(Mai
                }
        }
  
 -#ifdef WITH_LEGACY_DEPSGRAPH
 -      if (!DEG_depsgraph_use_legacy() && need_dependency_update)
 -#endif
 -      {
 +      if (relations_changed) {
                /* TODO(sergey): Consider doing partial update only. */
-               DEG_relations_tag_update(G.main);
 -              DAG_relations_tag_update(bmain);
++              DEG_relations_tag_update(bmain);
        }
  }
  
@@@ -1084,112 -1048,74 +1086,113 @@@ static short pose_grab_with_ik(Main *bm
        /* iTaSC needs clear for new IK constraints */
        if (tot_ik) {
                BIK_clear_data(ob->pose);
-               /* TODO(sergey): Consuder doing partial update only. */
-               DEG_relations_tag_update(G.main);
 -#ifdef WITH_LEGACY_DEPSGRAPH
 -              if (!DEG_depsgraph_use_legacy())
 -#endif
 -              {
 -                      /* TODO(sergey): Consider doing partial update only. */
 -                      DAG_relations_tag_update(bmain);
 -              }
++              /* TODO(sergey): Consider doing partial update only. */
++              DEG_relations_tag_update(bmain);
        }
  
        return (tot_ik) ? 1 : 0;
  }
  
  
 -/* only called with pose mode active object now */
 -static void createTransPose(TransInfo *t, Object *ob)
 +/**
 + * When objects array is NULL, use 't->data_container' as is.
 + */
 +static void createTransPose(TransInfo *t, Object **objects, uint objects_len)
  {
 -      bArmature *arm;
 -      bPoseChannel *pchan;
 -      TransData *td;
 -      TransDataExtension *tdx;
 -      short ik_on = 0;
 -      int i;
 +      if (objects != NULL) {
 +              if (t->data_container) {
 +                      MEM_freeN(t->data_container);
 +              }
 +              t->data_container = MEM_callocN(sizeof(*t->data_container) * objects_len, __func__);
 +              t->data_container_len = objects_len;
 +              int th_index;
 +              FOREACH_TRANS_DATA_CONTAINER_INDEX (t, tc, th_index) {
 +                      tc->poseobj = objects[th_index];
 +              }
 +      }
+       Main *bmain = CTX_data_main(t->context);
  
 -      t->total = 0;
 +      t->data_len_all = 0;
  
 -      /* check validity of state */
 -      arm = BKE_armature_from_object(ob);
 -      if ((arm == NULL) || (ob->pose == NULL)) return;
 +      bool has_translate_rotate_buf[2] = {false, false};
 +      bool *has_translate_rotate = (t->mode == TFM_TRANSLATION) ? has_translate_rotate_buf : NULL;
  
 -      if (arm->flag & ARM_RESTPOS) {
 -              if (ELEM(t->mode, TFM_DUMMY, TFM_BONESIZE) == 0) {
 -                      BKE_report(t->reports, RPT_ERROR, "Cannot change Pose when 'Rest Position' is enabled");
 -                      return;
 +      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +              Object *ob = tc->poseobj;
 +
 +              bArmature *arm;
 +              short ik_on = 0;
 +
 +              /* check validity of state */
 +              arm = BKE_armature_from_object(tc->poseobj);
 +              if ((arm == NULL) || (ob->pose == NULL)) {
 +                      continue;
 +              }
 +
 +              if (arm->flag & ARM_RESTPOS) {
 +                      if (ELEM(t->mode, TFM_DUMMY, TFM_BONESIZE) == 0) {
 +                              BKE_report(t->reports, RPT_ERROR, "Cannot change Pose when 'Rest Position' is enabled");
 +                              return;
 +                      }
 +              }
 +
 +              /* do we need to add temporal IK chains? */
 +              if ((arm->flag & ARM_AUTO_IK) && t->mode == TFM_TRANSLATION) {
-                       ik_on = pose_grab_with_ik(ob);
++                      ik_on = pose_grab_with_ik(bmain, ob);
 +                      if (ik_on) t->flag |= T_AUTOIK;
                }
 +
 +              /* set flags and count total (warning, can change transform to rotate) */
 +              tc->data_len = count_set_pose_transflags(ob, t->mode, t->around, has_translate_rotate);
 +              /* len may be zero, skip next iteration. */
        }
  
 -      /* do we need to add temporal IK chains? */
 -      if ((arm->flag & ARM_AUTO_IK) && t->mode == TFM_TRANSLATION) {
 -              ik_on = pose_grab_with_ik(bmain, ob);
 -              if (ik_on) t->flag |= T_AUTOIK;
 +      /* if there are no translatable bones, do rotation */
 +      if ((t->mode == TFM_TRANSLATION) && !has_translate_rotate[0]) {
 +              if (has_translate_rotate[1]) {
 +                      t->mode = TFM_ROTATION;
 +              }
 +              else {
 +                      t->mode = TFM_RESIZE;
 +              }
        }
  
 -      /* set flags and count total (warning, can change transform to rotate) */
 -      t->total = count_set_pose_transflags(&t->mode, t->around, ob);
 +      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +              if (tc->data_len == 0) {
 +                      continue;
 +              }
 +              Object *ob = tc->poseobj;
 +              TransData *td;
 +              TransDataExtension *tdx;
 +              short ik_on = 0;
 +              int i;
  
 -      if (t->total == 0) return;
 +              tc->poseobj = ob; /* we also allow non-active objects to be transformed, in weightpaint */
  
 -      t->flag |= T_POSE;
 -      t->poseobj = ob; /* we also allow non-active objects to be transformed, in weightpaint */
 +              /* init trans data */
 +              td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransPoseBone");
 +              tdx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransPoseBoneExt");
 +              for (i = 0; i < tc->data_len; i++, td++, tdx++) {
 +                      td->ext = tdx;
 +                      td->val = NULL;
 +              }
  
 -      /* disable PET, its not usable in pose mode yet [#32444] */
 -      t->flag &= ~T_PROP_EDIT_ALL;
 +              /* use pose channels to fill trans data */
 +              td = tc->data;
 +              for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
 +                      if (pchan->bone->flag & BONE_TRANSFORM) {
 +                              add_pose_transdata(t, pchan, ob, tc, td);
 +                              td++;
 +                      }
 +              }
  
 -      /* init trans data */
 -      td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransPoseBone");
 -      tdx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "TransPoseBoneExt");
 -      for (i = 0; i < t->total; i++, td++, tdx++) {
 -              td->ext = tdx;
 -              td->val = NULL;
 -      }
 +              if (td != (tc->data + tc->data_len)) {
 +                      BKE_report(t->reports, RPT_DEBUG, "Bone selection count error");
 +              }
  
 -      /* use pose channels to fill trans data */
 -      td = t->data;
 -      for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
 -              if (pchan->bone->flag & BONE_TRANSFORM) {
 -                      add_pose_transdata(t, pchan, ob, td);
 -                      td++;
 +              /* initialize initial auto=ik chainlen's? */
 +              if (ik_on) {
 +                      transform_autoik_update(t, 0);
                }
        }
  
@@@ -5776,36 -5611,47 +5779,35 @@@ static void trans_object_base_deps_flag
  /* it deselects Bases, so we have to call the clear function always after */
  static void set_trans_object_base_flags(TransInfo *t)
  {
-       /* TODO(sergey): Get rid of global, use explicit main. */
-       Main *bmain = G.main;
+       Main *bmain = CTX_data_main(t->context);
 +      ViewLayer *view_layer = t->view_layer;
        Scene *scene = t->scene;
 -      View3D *v3d = t->view;
 -
 -      /*
 -       * if Base selected and has parent selected:
 -       * base->flag = BA_WAS_SEL
 +      Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true);
 +      /* NOTE: if Base selected and has parent selected:
 +       *   base->flag_legacy = BA_WAS_SEL
         */
 -      Base *base;
 -
 -      /* don't do it if we're not actually going to recalculate anything */
 -      if (t->mode == TFM_DUMMY)
 +      /* Don't do it if we're not actually going to recalculate anything. */
 +      if (t->mode == TFM_DUMMY) {
                return;
 -
 -      /* makes sure base flags and object flags are identical */
 -      BKE_scene_base_flag_to_objects(t->scene);
 -
 -      /* Make sure depsgraph is here. */
 -      DAG_scene_relations_update(bmain, t->scene);
 -
 -      /* handle pending update events, otherwise they got copied below */
 -      for (base = scene->base.first; base; base = base->next) {
 -              if (base->object->recalc & OB_RECALC_ALL) {
 -                      /* TODO(sergey): Ideally, it's not needed. */
 -                      BKE_object_handle_update(bmain->eval_ctx, t->scene, base->object);
 -              }
        }
 -
 -      for (base = scene->base.first; base; base = base->next) {
 -              base->flag &= ~BA_WAS_SEL;
 -
 -              if (TESTBASELIB_BGMODE(v3d, scene, base)) {
 +      /* Makes sure base flags and object flags are identical. */
 +      BKE_scene_base_flag_to_objects(t->view_layer);
 +      /* Make sure depsgraph is here. */
 +      DEG_graph_relations_update(depsgraph, bmain, scene, view_layer);
 +      /* Clear all flags we need. It will be used to detect dependencies. */
 +      trans_object_base_deps_flag_prepare(view_layer);
 +      /* Traverse all bases and set all possible flags. */
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              base->flag_legacy &= ~BA_WAS_SEL;
 +              if (TESTBASELIB_BGMODE(base)) {
                        Object *ob = base->object;
                        Object *parsel = ob->parent;
 -
 -                      /* if parent selected, deselect */
 -                      while (parsel) {
 -                              if (parsel->flag & SELECT) {
 -                                      Base *parbase = BKE_scene_base_find(scene, parsel);
 -                                      if (parbase) { /* in rare cases this can fail */
 -                                              if (TESTBASELIB_BGMODE(v3d, scene, parbase)) {
 +                      /* If parent selected, deselect. */
 +                      while (parsel != NULL) {
 +                              if (parsel->base_flag & BASE_SELECTED) {
 +                                      Base *parbase = BKE_view_layer_base_find(view_layer, parsel);
 +                                      if (parbase != NULL) { /* in rare cases this can fail */
 +                                              if (TESTBASELIB_BGMODE(parbase)) {
                                                        break;
                                                }
                                        }
@@@ -6655,72 -6506,60 +6660,72 @@@ void special_aftertrans_update(bContex
                        ED_nla_postop_refresh(&ac);
                }
        }
 -      else if (t->obedit) {
 -              if (t->obedit->type == OB_MESH) {
 -                      BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
 -                      /* table needs to be created for each edit command, since vertices can move etc */
 -                      ED_mesh_mirror_spatial_table(t->obedit, em, NULL, NULL, 'e');
 +      else if (t->flag & T_EDIT) {
 +              if (t->obedit_type == OB_MESH) {
 +                      FOREACH_TRANS_DATA_CONTAINER (t, tc) {
 +                              BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
 +                              /* table needs to be created for each edit command, since vertices can move etc */
 +                              ED_mesh_mirror_spatial_table(tc->obedit, em, NULL, NULL, 'e');
 +                              /* TODO(campbell): xform: We need support for many mirror objects at once! */
 +                              break;
 +                      }
                }
        }
 -      else if ((t->flag & T_POSE) && (t->poseobj)) {
 -              bArmature *arm;
 -              bPoseChannel *pchan;
 -              short targetless_ik = 0;
 +      else if (t->flag & T_POSE) {
  
 -              ob = t->poseobj;
 -              arm = ob->data;
 +              FOREACH_TRANS_DATA_CONTAINER (t, tc) {
  
 -              if ((t->flag & T_AUTOIK) && (t->options & CTX_AUTOCONFIRM)) {
 -                      /* when running transform non-interactively (operator exec),
 -                       * we need to update the pose otherwise no updates get called during
 -                       * transform and the auto-ik is not applied. see [#26164] */
 -                      struct Object *pose_ob = t->poseobj;
 -                      BKE_pose_where_is(t->scene, pose_ob);
 -              }
 +                      bArmature *arm;
 +                      bPoseChannel *pchan;
 +                      short targetless_ik = 0;
  
 -              /* set BONE_TRANSFORM flags for autokey, manipulator draw might have changed them */
 -              if (!canceled && (t->mode != TFM_DUMMY))
 -                      count_set_pose_transflags(&t->mode, t->around, ob);
 +                      ob = tc->poseobj;
 +                      arm = ob->data;
  
 -              /* if target-less IK grabbing, we calculate the pchan transforms and clear flag */
 -              if (!canceled && t->mode == TFM_TRANSLATION)
 -                      targetless_ik = apply_targetless_ik(ob);
 -              else {
 -                      /* not forget to clear the auto flag */
 -                      for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
 -                              bKinematicConstraint *data = has_targetless_ik(pchan);
 -                              if (data) data->flag &= ~CONSTRAINT_IK_AUTO;
 +                      if ((t->flag & T_AUTOIK) && (t->options & CTX_AUTOCONFIRM)) {
 +                              /* when running transform non-interactively (operator exec),
 +                               * we need to update the pose otherwise no updates get called during
 +                               * transform and the auto-ik is not applied. see [#26164] */
 +                              struct Object *pose_ob = tc->poseobj;
 +                              BKE_pose_where_is(t->depsgraph, t->scene, pose_ob);
                        }
 -              }
  
 -              if (t->mode == TFM_TRANSLATION)
 -                      pose_grab_with_ik_clear(bmain, ob);
 +                      /* set BONE_TRANSFORM flags for autokey, manipulator draw might have changed them */
 +                      if (!canceled && (t->mode != TFM_DUMMY)) {
 +                              count_set_pose_transflags(ob, t->mode, t->around, NULL);
 +                      }
  
 -              /* automatic inserting of keys and unkeyed tagging - only if transform wasn't canceled (or TFM_DUMMY) */
 -              if (!canceled && (t->mode != TFM_DUMMY)) {
 -                      autokeyframe_pose_cb_func(C, t->scene, (View3D *)t->view, ob, t->mode, targetless_ik);
 -                      DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
 -              }
 -              else if (arm->flag & ARM_DELAYDEFORM) {
 -                      /* old optimize trick... this enforces to bypass the depgraph */
 -                      DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
 -                      ob->recalc = 0;  // is set on OK position already by recalcData()
 -              }
 -              else
 -                      DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
 +                      /* if target-less IK grabbing, we calculate the pchan transforms and clear flag */
 +                      if (!canceled && t->mode == TFM_TRANSLATION)
 +                              targetless_ik = apply_targetless_ik(ob);
 +                      else {
 +                              /* not forget to clear the auto flag */
 +                              for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
 +                                      bKinematicConstraint *data = has_targetless_ik(pchan);
 +                                      if (data) data->flag &= ~CONSTRAINT_IK_AUTO;
 +                              }
 +                      }
 +
 +                      if (t->mode == TFM_TRANSLATION)
-                               pose_grab_with_ik_clear(ob);
++                              pose_grab_with_ik_clear(bmain, ob);
  
 +                      /* automatic inserting of keys and unkeyed tagging - only if transform wasn't canceled (or TFM_DUMMY) */
 +                      if (!canceled && (t->mode != TFM_DUMMY)) {
 +                              autokeyframe_pose_cb_func(C, t->scene, ob, t->mode, targetless_ik);
 +                              DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
 +                      }
 +                      else if (arm->flag & ARM_DELAYDEFORM) {
 +                              /* TODO(sergey): Armature is already updated by recalcData(), so we
 +                               * might save some time by skipping re-evaluating it. But this isn't
 +                               * possible yet within new dependency graph, and also other contexts
 +                               * might need to update their CoW copies.
 +                               */
 +                              DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
 +                      }
 +                      else {
 +                              DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
 +                      }
 +              }
        }
        else if (t->options & CTX_PAINT_CURVE) {
                /* pass */
@@@ -270,77 -284,77 +270,79 @@@ eRedrawFlag handleSnapping(TransInfo *t
  
  void applyProject(TransInfo *t)
  {
+       Main *bmain = CTX_data_main(t->context);
        /* XXX FLICKER IN OBJECT MODE */
        if ((t->tsnap.project) && activeSnap(t) && (t->flag & T_NO_PROJECT) == 0) {
 -              TransData *td = t->data;
                float tvec[3];
 -              float imat[4][4];
                int i;
  
 -              if (t->flag & (T_EDIT | T_POSE)) {
 -                      Object *ob = t->obedit ? t->obedit : t->poseobj;
 -                      invert_m4_m4(imat, ob->obmat);
 -              }
 -
 -              for (i = 0; i < t->total; i++, td++) {
 -                      float iloc[3], loc[3], no[3];
 -                      float mval_fl[2];
 -                      float dist_px = TRANSFORM_DIST_MAX_PX;
 +              FOREACH_TRANS_DATA_CONTAINER(t, tc) {
 +                      TransData *td = tc->data;
 +                      for (i = 0; i < tc->data_len; i++, td++) {
 +                              float iloc[3], loc[3], no[3];
 +                              float mval_fl[2];
  
 -                      if (td->flag & TD_NOACTION)
 -                              break;
 +                              if (td->flag & TD_NOACTION)
 +                                      break;
  
 -                      if (td->flag & TD_SKIP)
 -                              continue;
 +                              if (td->flag & TD_SKIP)
 +                                      continue;
  
 -                      if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f))
 -                              continue;
 +                              if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f))
 +                                      continue;
  
 -                      copy_v3_v3(iloc, td->loc);
 -                      if (t->flag & (T_EDIT | T_POSE)) {
 -                              Object *ob = t->obedit ? t->obedit : t->poseobj;
 -                              mul_m4_v3(ob->obmat, iloc);
 -                      }
 -                      else if (t->flag & T_OBJECT) {
 -                              BKE_object_eval_transform_all(bmain->eval_ctx, t->scene, td->ob);
 -                              copy_v3_v3(iloc, td->ob->obmat[3]);
 -                      }
 +                              copy_v3_v3(iloc, td->loc);
 +                              if (tc->use_local_mat) {
 +                                      mul_m4_v3(tc->mat, iloc);
 +                              }
 +                              else if (t->flag & T_OBJECT) {
 +                                      BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob);
 +                                      copy_v3_v3(iloc, td->ob->obmat[3]);
 +                              }
  
 -                      if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
 -                              if (snapObjectsTransform(
 -                                      t, mval_fl, &dist_px,
 -                                      loc, no))
 -                              {
 -//                                    if (t->flag & (T_EDIT|T_POSE)) {
 -//                                            mul_m4_v3(imat, loc);
 -//                                    }
 +                              if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
 +                                      if (ED_transform_snap_object_project_view3d(
 +                                              t->tsnap.object_context,
 +                                              SCE_SNAP_MODE_FACE,
 +                                              &(const struct SnapObjectParams){
 +                                                  .snap_select = t->tsnap.modeSelect,
 +                                                  .use_object_edit_cage = (t->flag & T_EDIT) != 0,
 +                                                  .use_occlusion_test = false,
 +                                              },
 +                                              mval_fl, 0, loc, no))
 +                                      {
 +#if 0
 +                                              if (tc->use_local_mat) {
 +                                                      mul_m4_v3(tc->imat, loc);
 +                                              }
 +#endif
  
 -                                      sub_v3_v3v3(tvec, loc, iloc);
 +                                              sub_v3_v3v3(tvec, loc, iloc);
  
 -                                      mul_m3_v3(td->smtx, tvec);
 +                                              mul_m3_v3(td->smtx, tvec);
  
 -                                      add_v3_v3(td->loc, tvec);
 +                                              add_v3_v3(td->loc, tvec);
  
 -                                      if (t->tsnap.align && (t->flag & T_OBJECT)) {
 -                                              /* handle alignment as well */
 -                                              const float *original_normal;
 -                                              float mat[3][3];
 +                                              if (t->tsnap.align && (t->flag & T_OBJECT)) {
 +                                                      /* handle alignment as well */
 +                                                      const float *original_normal;
 +                                                      float mat[3][3];
  
 -                                              /* In pose mode, we want to align normals with Y axis of bones... */
 -                                              original_normal = td->axismtx[2];
 +                                                      /* In pose mode, we want to align normals with Y axis of bones... */
 +                                                      original_normal = td->axismtx[2];
  
 -                                              rotation_between_vecs_to_mat3(mat, original_normal, no);
 +                                                      rotation_between_vecs_to_mat3(mat, original_normal, no);
  
 -                                              transform_data_ext_rotate(td, mat, true);
 +                                                      transform_data_ext_rotate(td, mat, true);
  
 -                                              /* TODO support constraints for rotation too? see ElementRotation */
 +                                                      /* TODO support constraints for rotation too? see ElementRotation */
 +                                              }
                                        }
                                }
 -                      }
  
 -                      //XXX constraintTransLim(t, td);
 +                              //XXX constraintTransLim(t, td);
 +                      }
                }
        }
  }
@@@ -500,11 -518,11 +502,12 @@@ static bool bm_face_is_snap_target(BMFa
  
  static void initSnappingMode(TransInfo *t)
  {
+       Main *bmain = CTX_data_main(t->context);
        ToolSettings *ts = t->settings;
 -      Object *obedit = t->obedit;
 -      Scene *scene = t->scene;
 -      Base *base_act = scene->basact;
 +      /* All obedit types will match. */
 +      const int obedit_type = t->data_container->obedit ? t->data_container->obedit->type : -1;
 +      ViewLayer *view_layer = t->view_layer;
 +      Base *base_act = view_layer->basact;
  
        if (t->spacetype == SPACE_NODE) {
                /* force project off when not supported */
        if (t->spacetype == SPACE_VIEW3D) {
                if (t->tsnap.object_context == NULL) {
                        t->tsnap.object_context = ED_transform_snap_object_context_create_view3d(
-                               t->scene, t->depsgraph, 0, t->ar, t->view);
 -                              bmain, t->scene, 0,
 -                              t->ar, t->view);
++                              bmain, t->scene, t->depsgraph, 0, t->ar, t->view);
  
                        ED_transform_snap_object_context_set_editmesh_callbacks(
                                t->tsnap.object_context,
@@@ -114,9 -103,8 +114,10 @@@ typedef struct SnapObjectData_EditMesh 
  } SnapObjectData_EditMesh;
  
  struct SnapObjectContext {
+       Main *bmain;
        Scene *scene;
 +      Depsgraph *depsgraph;
 +
        int flag;
  
        /* Optional: when performing screen-space projection.
@@@ -2270,14 -2075,14 +2271,15 @@@ static short snapObjectsRay
   * \{ */
  
  SnapObjectContext *ED_transform_snap_object_context_create(
-         Scene *scene, Depsgraph *depsgraph, int flag)
 -        Main *bmain, Scene *scene, int flag)
++        Main *bmain, Scene *scene, Depsgraph *depsgraph, int flag)
  {
        SnapObjectContext *sctx = MEM_callocN(sizeof(*sctx), __func__);
  
        sctx->flag = flag;
  
+       sctx->bmain = bmain;
        sctx->scene = scene;
 +      sctx->depsgraph = depsgraph;
  
        sctx->cache.object_map = BLI_ghash_ptr_new(__func__);
        sctx->cache.mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
  }
  
  SnapObjectContext *ED_transform_snap_object_context_create_view3d(
-         Scene *scene, Depsgraph *depsgraph, int flag,
 -        Main *bmain, Scene *scene, int flag,
++        Main *bmain, Scene *scene, Depsgraph *depsgraph, int flag,
          /* extra args for view3d */
          const ARegion *ar, const View3D *v3d)
  {
-       SnapObjectContext *sctx = ED_transform_snap_object_context_create(scene, depsgraph, flag);
 -      SnapObjectContext *sctx = ED_transform_snap_object_context_create(bmain, scene, flag);
++      SnapObjectContext *sctx = ED_transform_snap_object_context_create(bmain, scene, depsgraph, flag);
  
        sctx->use_v3d = true;
        sctx->v3d_data.ar = ar;
@@@ -164,16 -154,14 +164,15 @@@ static void rna_SceneRender_get_frame_p
  }
  
  static void rna_Scene_ray_cast(
-         Scene *scene, ViewLayer *view_layer,
 -        Scene *scene, Main *bmain,
++        Scene *scene, Main *bmain, ViewLayer *view_layer,
          float origin[3], float direction[3], float ray_dist,
          int *r_success, float r_location[3], float r_normal[3], int *r_index,
          Object **r_ob, float r_obmat[16])
  {
        normalize_v3(direction);
  
 -      SnapObjectContext *sctx = ED_transform_snap_object_context_create(bmain, scene, 0);
 +      Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true);
-       SnapObjectContext *sctx = ED_transform_snap_object_context_create(
-               scene, depsgraph, 0);
++      SnapObjectContext *sctx = ED_transform_snap_object_context_create(bmain, scene, depsgraph, 0);
  
        bool ret = ED_transform_snap_object_project_ray_ex(
                sctx,
@@@ -309,9 -297,8 +308,10 @@@ void RNA_api_scene(StructRNA *srna
        
        /* Ray Cast */
        func = RNA_def_function(srna, "ray_cast", "rna_Scene_ray_cast");
+       RNA_def_function_flag(func, FUNC_USE_MAIN);
        RNA_def_function_ui_description(func, "Cast a ray onto in object space");
 +      parm = RNA_def_pointer(func, "view_layer", "ViewLayer", "", "Scene Layer");
 +      RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
        /* ray start and end */
        parm = RNA_def_float_vector(func, "origin", 3, NULL, -FLT_MAX, FLT_MAX, "", "", -1e4, 1e4);
        RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
  
  #include "WM_types.h"
  
+ #include "BKE_global.h"
  #include "ED_screen.h"
  
 -#include "GPU_compositing.h"
  #include "GPU_framebuffer.h"
 +#include "GPU_texture.h"
  
  #include "../mathutils/mathutils.h"
  
@@@ -1157,9 -1146,9 +1157,9 @@@ static int wm_file_write(bContext *C, c
        /* blend file thumbnail */
        /* save before exit_editmode, otherwise derivedmeshes for shared data corrupt #27765) */
        /* Main now can store a .blend thumbnail, usefull for background mode or thumbnail customization. */
--      main_thumb = thumb = CTX_data_main(C)->blen_thumb;
++      main_thumb = thumb = bmain->blen_thumb;
        if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) {
 -              ibuf_thumb = blend_file_thumb(bmain, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
 +              ibuf_thumb = blend_file_thumb(C, CTX_data_scene(C), CTX_wm_screen(C), &thumb);
        }
  
        /* operator now handles overwrite checks */