WM: refactor gestures for use as tools
[blender.git] / source / blender / editors / space_view3d / view3d_edit.c
index 8f5c90956797d6c9679ed35e700c26f69cd722ba..77a3556db0a7c34a4707876273753386d8a00d81 100644 (file)
 #include "DNA_curve_types.h"
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
+#include "DNA_gpencil_types.h"
 
 #include "MEM_guardedalloc.h"
 
+#include "BLI_bitmap_draw_2d.h"
 #include "BLI_blenlib.h"
+#include "BLI_kdopbvh.h"
 #include "BLI_math.h"
 #include "BLI_utildefines.h"
 
@@ -73,6 +76,7 @@
 #include "ED_screen.h"
 #include "ED_transform.h"
 #include "ED_mesh.h"
+#include "ED_gpencil.h"
 #include "ED_view3d.h"
 
 #include "UI_resources.h"
 
 #include "view3d_intern.h"  /* own include */
 
+static bool view3d_ensure_persp(struct View3D *v3d, ARegion *ar);
+
 bool ED_view3d_offset_lock_check(const  View3D *v3d, const  RegionView3D *rv3d)
 {
        return (rv3d->persp != RV3D_CAMOB) && (v3d->ob_centre_cursor || v3d->ob_centre);
 }
 
-static bool view3d_operator_offset_lock_check(bContext *C, wmOperator *op)
-{
-       View3D *v3d = CTX_wm_view3d(C);
-       RegionView3D *rv3d = CTX_wm_region_view3d(C);
-       if (ED_view3d_offset_lock_check(v3d, rv3d)) {
-               BKE_report(op->reports, RPT_WARNING, "View offset is locked");
-               return true;
-       }
-       else {
-               return false;
-       }
-}
-
 /* ********************** view3d_edit: view manipulations ********************* */
 
 /**
@@ -107,7 +100,7 @@ static bool view3d_operator_offset_lock_check(bContext *C, wmOperator *op)
 bool ED_view3d_camera_lock_check(const View3D *v3d, const RegionView3D *rv3d)
 {
        return ((v3d->camera) &&
-               (v3d->camera->id.lib == NULL) &&
+               (!ID_IS_LINKED_DATABLOCK(v3d->camera)) &&
                (v3d->flag2 & V3D_LOCK_CAMERA) &&
                (rv3d->persp == RV3D_CAMOB));
 }
@@ -562,22 +555,20 @@ typedef struct ViewOpsData {
 
 static void calctrackballvec(const rcti *rect, int mx, int my, float vec[3])
 {
-       float x, y, radius, d, z, t;
-
-       radius = TRACKBALLSIZE;
+       const float radius = TRACKBALLSIZE;
+       const float t = radius / (float)M_SQRT2;
+       float x, y, z, d;
 
        /* normalize x and y */
        x = BLI_rcti_cent_x(rect) - mx;
        x /= (float)(BLI_rcti_size_x(rect) / 4);
        y = BLI_rcti_cent_y(rect) - my;
        y /= (float)(BLI_rcti_size_y(rect) / 2);
-
        d = sqrtf(x * x + y * y);
-       if (d < radius * (float)M_SQRT1_2) { /* Inside sphere */
+       if (d < t) { /* Inside sphere */
                z = sqrtf(radius * radius - d * d);
        }
        else { /* On hyperbola */
-               t = radius / (float)M_SQRT2;
                z = t * t / d;
        }
 
@@ -609,18 +600,18 @@ static void viewops_data_alloc(bContext *C, wmOperator *op)
        vod->rv3d = vod->ar->regiondata;
 }
 
-static void view3d_orbit_apply_dyn_ofs(
-        float r_ofs[3], const float dyn_ofs[3],
-        const float oldquat[4], const float viewquat[4])
+void view3d_orbit_apply_dyn_ofs(
+        float r_ofs[3], const float ofs_old[3], const float viewquat_old[4],
+        const float viewquat_new[4], const float dyn_ofs[3])
 {
-       float q1[4];
-       invert_qt_qt_normalized(q1, oldquat);
-       mul_qt_qtqt(q1, q1, viewquat);
+       float q[4];
+       invert_qt_qt_normalized(q, viewquat_old);
+       mul_qt_qtqt(q, q, viewquat_new);
 
-       invert_qt_normalized(q1);
+       invert_qt_normalized(q);
 
-       sub_v3_v3(r_ofs, dyn_ofs);
-       mul_qt_v3(q1, r_ofs);
+       sub_v3_v3v3(r_ofs, ofs_old, dyn_ofs);
+       mul_qt_v3(q, r_ofs);
        add_v3_v3(r_ofs, dyn_ofs);
 }
 
@@ -630,28 +621,28 @@ static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
        bool is_set = false;
 
        Scene *scene = CTX_data_scene(C);
-       Object *ob = OBACT;
+       Object *ob_act = OBACT;
 
-       if (ob && (ob->mode & OB_MODE_ALL_PAINT) &&
+       if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) &&
            /* with weight-paint + pose-mode, fall through to using calculateTransformCenter */
-           ((ob->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob)) == 0)
+           ((ob_act->mode & OB_MODE_WEIGHT_PAINT) && BKE_object_pose_armature_get(ob_act)) == 0)
        {
                /* in case of sculpting use last average stroke position as a rotation
                 * center, in other cases it's not clear what rotation center shall be
                 * so just rotate around object origin
                 */
-               if (ob->mode & (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
+               if (ob_act->mode & (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
                        float stroke[3];
-                       BKE_paint_stroke_get_average(scene, ob, stroke);
+                       BKE_paint_stroke_get_average(scene, ob_act, stroke);
                        copy_v3_v3(lastofs, stroke);
                }
                else {
-                       copy_v3_v3(lastofs, ob->obmat[3]);
+                       copy_v3_v3(lastofs, ob_act->obmat[3]);
                }
                is_set = true;
        }
-       else if (ob && (ob->mode & OB_MODE_EDIT) && (ob->type == OB_FONT)) {
-               Curve *cu = ob->data;
+       else if (ob_act && (ob_act->mode & OB_MODE_EDIT) && (ob_act->type == OB_FONT)) {
+               Curve *cu = ob_act->data;
                EditFont *ef = cu->editfont;
                int i;
 
@@ -661,11 +652,11 @@ static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
                }
                mul_v2_fl(lastofs, 1.0f / 4.0f);
 
-               mul_m4_v3(ob->obmat, lastofs);
+               mul_m4_v3(ob_act->obmat, lastofs);
 
                is_set = true;
        }
-       else if (ob == NULL || ob->mode == OB_MODE_OBJECT) {
+       else if (ob_act == NULL || ob_act->mode == OB_MODE_OBJECT) {
                /* object mode use boundbox centers */
                View3D *v3d = CTX_wm_view3d(C);
                Base *base;
@@ -700,7 +691,7 @@ static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
        }
        else {
                /* If there's no selection, lastofs is unmodified and last value since static */
-               is_set = calculateTransformCenter(C, V3D_CENTROID, lastofs, NULL);
+               is_set = calculateTransformCenter(C, V3D_AROUND_CENTER_MEAN, lastofs, NULL);
        }
 
        copy_v3_v3(r_dyn_ofs, lastofs);
@@ -708,16 +699,72 @@ static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
        return is_set;
 }
 
+enum eViewOpsOrbit {
+       VIEWOPS_ORBIT_SELECT = (1 << 0),
+       VIEWOPS_ORBIT_DEPTH = (1 << 1),
+};
+
+static enum eViewOpsOrbit viewops_orbit_mode_ex(bool use_select, bool use_depth)
+{
+       enum eViewOpsOrbit flag = 0;
+       if (use_select) {
+               flag |= VIEWOPS_ORBIT_SELECT;
+       }
+       if (use_depth) {
+               flag |= VIEWOPS_ORBIT_DEPTH;
+       }
+
+       return flag;
+}
+
+static enum eViewOpsOrbit viewops_orbit_mode(void)
+{
+       return viewops_orbit_mode_ex(
+               (U.uiflag & USER_ORBIT_SELECTION) != 0,
+               (U.uiflag & USER_ZBUF_ORBIT) != 0);
+}
+
 /**
  * Calculate the values for #ViewOpsData
+ *
+ * \param use_ensure_persp: When enabled run #view3d_ensure_persp this may switch out of
+ * camera view when orbiting or switch from ortho to perspective when auto-persp is enabled.
+ * Some operations don't require this (view zoom/pan or ndof where subtle rotation is common
+ * so we don't want it to trigger auto-perspective).
  */
-static void viewops_data_create_ex(bContext *C, wmOperator *op, const wmEvent *event,
-                                   const bool use_orbit_select,
-                                   const bool use_orbit_zbuf)
+static void viewops_data_create_ex(
+        bContext *C, wmOperator *op, const wmEvent *event,
+        bool use_ensure_persp, enum eViewOpsOrbit orbit_mode)
 {
        ViewOpsData *vod = op->customdata;
        RegionView3D *rv3d = vod->rv3d;
 
+       /* we need the depth info before changing any viewport options */
+       if (orbit_mode & VIEWOPS_ORBIT_DEPTH) {
+               float fallback_depth_pt[3];
+
+               view3d_operator_needs_opengl(C); /* needed for zbuf drawing */
+
+               negate_v3_v3(fallback_depth_pt, rv3d->ofs);
+
+               vod->use_dyn_ofs = ED_view3d_autodist(
+                       vod->scene, vod->ar, vod->v3d,
+                       event->mval, vod->dyn_ofs, true, fallback_depth_pt);
+       }
+       else {
+               vod->use_dyn_ofs = false;
+       }
+
+       if (use_ensure_persp) {
+               if (view3d_ensure_persp(vod->v3d, vod->ar)) {
+                       /* If we're switching from camera view to the perspective one,
+                        * need to tag viewport update, so camera vuew and borders
+                        * are properly updated.
+                        */
+                       ED_region_tag_redraw(vod->ar);
+               }
+       }
+
        /* set the view from the camera, if view locking is enabled.
         * we may want to make this optional but for now its needed always */
        ED_view3d_camera_lock_init(vod->v3d, vod->rv3d);
@@ -729,28 +776,19 @@ static void viewops_data_create_ex(bContext *C, wmOperator *op, const wmEvent *e
        vod->origx = vod->oldx = event->x;
        vod->origy = vod->oldy = event->y;
        vod->origkey = event->type; /* the key that triggered the operator.  */
-       vod->use_dyn_ofs = false;
        copy_v3_v3(vod->ofs, rv3d->ofs);
 
-       if (use_orbit_select) {
-
-               vod->use_dyn_ofs = true;
-
-               view3d_orbit_calc_center(C, vod->dyn_ofs);
-
-               negate_v3(vod->dyn_ofs);
+       if (orbit_mode & VIEWOPS_ORBIT_SELECT) {
+               float ofs[3];
+               if (view3d_orbit_calc_center(C, ofs) || (vod->use_dyn_ofs == false)) {
+                       vod->use_dyn_ofs = true;
+                       negate_v3_v3(vod->dyn_ofs, ofs);
+                       orbit_mode &= ~VIEWOPS_ORBIT_DEPTH;
+               }
        }
-       else if (use_orbit_zbuf) {
-               Scene *scene = CTX_data_scene(C);
-               float fallback_depth_pt[3];
 
-               view3d_operator_needs_opengl(C); /* needed for zbuf drawing */
-
-               negate_v3_v3(fallback_depth_pt, rv3d->ofs);
-
-               if ((vod->use_dyn_ofs = ED_view3d_autodist(scene, vod->ar, vod->v3d,
-                                                          event->mval, vod->dyn_ofs, true, fallback_depth_pt)))
-               {
+       if (orbit_mode & VIEWOPS_ORBIT_DEPTH) {
+               if (vod->use_dyn_ofs) {
                        if (rv3d->is_persp) {
                                float my_origin[3]; /* original G.vd->ofs */
                                float my_pivot[3]; /* view */
@@ -785,7 +823,7 @@ static void viewops_data_create_ex(bContext *C, wmOperator *op, const wmEvent *e
                                    (float)vod->ar->winx / 2.0f,
                                    (float)vod->ar->winy / 2.0f};
 
-                               ED_view3d_win_to_3d(vod->ar, vod->dyn_ofs, mval_ar_mid, rv3d->ofs);
+                               ED_view3d_win_to_3d(vod->v3d, vod->ar, vod->dyn_ofs, mval_ar_mid, rv3d->ofs);
                                negate_v3(rv3d->ofs);
                        }
                        negate_v3(vod->dyn_ofs);
@@ -819,12 +857,10 @@ static void viewops_data_create_ex(bContext *C, wmOperator *op, const wmEvent *e
        rv3d->rflag |= RV3D_NAVIGATING;
 }
 
-static void viewops_data_create(bContext *C, wmOperator *op, const wmEvent *event)
+static void viewops_data_create(bContext *C, wmOperator *op, const wmEvent *event, bool use_ensure_persp)
 {
-       viewops_data_create_ex(
-               C, op, event,
-               (U.uiflag & USER_ORBIT_SELECTION) != 0,
-               (U.uiflag & USER_ZBUF_ORBIT) != 0);
+       enum eViewOpsOrbit orbit_mode = viewops_orbit_mode();
+       viewops_data_create_ex(C, op, event, use_ensure_persp, orbit_mode);
 }
 
 static void viewops_data_free(bContext *C, wmOperator *op)
@@ -910,12 +946,11 @@ void viewrotate_modal_keymap(wmKeyConfig *keyconf)
 
 }
 
-static void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat[4])
+static void viewrotate_apply_dyn_ofs(ViewOpsData *vod, const float viewquat_new[4])
 {
        if (vod->use_dyn_ofs) {
                RegionView3D *rv3d = vod->rv3d;
-               copy_v3_v3(rv3d->ofs, vod->ofs);
-               view3d_orbit_apply_dyn_ofs(rv3d->ofs, vod->dyn_ofs, vod->oldquat, viewquat);
+               view3d_orbit_apply_dyn_ofs(rv3d->ofs, vod->ofs, vod->oldquat, viewquat_new, vod->dyn_ofs);
        }
 }
 
@@ -1221,7 +1256,6 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 
        /* makes op->customdata */
        viewops_data_alloc(C, op);
-       viewops_data_create(C, op, event);
        vod = op->customdata;
 
        /* poll should check but in some cases fails, see poll func for details */
@@ -1230,14 +1264,9 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
                return OPERATOR_PASS_THROUGH;
        }
 
-       /* switch from camera view when: */
-       if (view3d_ensure_persp(vod->v3d, vod->ar)) {
-               /* If we're switching from camera view to the perspective one,
-                * need to tag viewport update, so camera vuew and borders
-                * are properly updated.
-                */
-               ED_region_tag_redraw(vod->ar);
-       }
+       ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
+
+       viewops_data_create(C, op, event, true);
 
        if (ELEM(event->type, MOUSEPAN, MOUSEROTATE)) {
                /* Rotate direction we keep always same */
@@ -1324,6 +1353,8 @@ void VIEW3D_OT_rotate(wmOperatorType *ot)
        ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
 }
 
+#ifdef WITH_INPUT_NDOF
+
 /** \name NDOF Utility Functions
  * \{ */
 
@@ -1644,10 +1675,13 @@ static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
                const wmNDOFMotionData *ndof = event->customdata;
 
                viewops_data_alloc(C, op);
-               viewops_data_create_ex(C, op, event,
-                                      (U.uiflag & USER_ORBIT_SELECTION) != 0, false);
-
+               viewops_data_create_ex(
+                       C, op, event,
+                       false, viewops_orbit_mode_ex((U.uiflag & USER_ORBIT_SELECTION) != 0, false));
                vod = op->customdata;
+
+               ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
+
                v3d = vod->v3d;
                rv3d = vod->rv3d;
 
@@ -1710,10 +1744,14 @@ static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *ev
                const wmNDOFMotionData *ndof = event->customdata;
 
                viewops_data_alloc(C, op);
-               viewops_data_create_ex(C, op, event,
-                                      (U.uiflag & USER_ORBIT_SELECTION) != 0, false);
+               viewops_data_create_ex(
+                       C, op, event,
+                       false, viewops_orbit_mode_ex((U.uiflag & USER_ORBIT_SELECTION) != 0, false));
 
                vod = op->customdata;
+
+               ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
+
                v3d = vod->v3d;
                rv3d = vod->rv3d;
 
@@ -1884,6 +1922,8 @@ void VIEW3D_OT_ndof_all(struct wmOperatorType *ot)
        ot->flag = 0;
 }
 
+#endif /* WITH_INPUT_NDOF */
+
 /* ************************ viewmove ******************************** */
 
 
@@ -2020,9 +2060,11 @@ static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 
        /* makes op->customdata */
        viewops_data_alloc(C, op);
-       viewops_data_create(C, op, event);
+       viewops_data_create(C, op, event, false);
        vod = op->customdata;
 
+       ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
+
        if (event->type == MOUSEPAN) {
                /* invert it, trackpad scroll follows same principle as 2d windows this way */
                viewmove_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy);
@@ -2499,9 +2541,11 @@ static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 
        /* makes op->customdata */
        viewops_data_alloc(C, op);
-       viewops_data_create(C, op, event);
+       viewops_data_create(C, op, event, false);
        vod = op->customdata;
 
+       ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
+
        /* if one or the other zoom position aren't set, set from event */
        if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
                RNA_int_set(op->ptr, "mx", event->x);
@@ -2579,6 +2623,19 @@ void VIEW3D_OT_zoom(wmOperatorType *ot)
 
 
 /* ************************ viewdolly ******************************** */
+static bool viewdolly_offset_lock_check(bContext *C, wmOperator *op)
+{
+       View3D *v3d = CTX_wm_view3d(C);
+       RegionView3D *rv3d = CTX_wm_region_view3d(C);
+       if (ED_view3d_offset_lock_check(v3d, rv3d)) {
+               BKE_report(op->reports, RPT_WARNING, "Cannot dolly when the view offset is locked");
+               return true;
+       }
+       else {
+               return false;
+       }
+}
+
 static void view_dolly_mouseloc(ARegion *ar, float orig_ofs[3], float dvec[3], float dfac)
 {
        RegionView3D *rv3d = ar->regiondata;
@@ -2729,7 +2786,7 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
        ViewOpsData *vod;
 
-       if (view3d_operator_offset_lock_check(C, op))
+       if (viewdolly_offset_lock_check(C, op))
                return OPERATOR_CANCELLED;
 
        /* makes op->customdata */
@@ -2742,6 +2799,8 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
                return OPERATOR_PASS_THROUGH;
        }
 
+       ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
+
        /* needs to run before 'viewops_data_create' so the backup 'rv3d->ofs' is correct */
        /* switch from camera view when: */
        if (vod->rv3d->persp != RV3D_PERSP) {
@@ -2755,7 +2814,7 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
                ED_region_tag_redraw(vod->ar);
        }
 
-       viewops_data_create(C, op, event);
+       viewops_data_create(C, op, event, false);
 
 
        /* if one or the other zoom position aren't set, set from event */
@@ -2837,6 +2896,8 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar,
        float afm[3];
        float size;
 
+       ED_view3d_smooth_view_force_finish(C, v3d, ar);
+
        /* SMOOTHVIEW */
        float new_ofs[3];
        float new_dist;
@@ -2880,14 +2941,16 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar,
 
        if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) {
                rv3d->persp = RV3D_PERSP;
-               ED_view3d_smooth_view(C, v3d, ar, v3d->camera, NULL,
-                                     new_ofs, NULL, ok_dist ? &new_dist : NULL, NULL,
-                                     smooth_viewtx);
+               ED_view3d_smooth_view(
+                       C, v3d, ar, smooth_viewtx,
+                       &(const V3D_SmoothParams) {
+                           .camera_old = v3d->camera, .ofs = new_ofs,
+                           .dist = ok_dist ? &new_dist : NULL});
        }
        else {
-               ED_view3d_smooth_view(C, v3d, ar, NULL, NULL,
-                                     new_ofs, NULL, ok_dist ? &new_dist : NULL, NULL,
-                                     smooth_viewtx);
+               ED_view3d_smooth_view(
+                       C, v3d, ar, smooth_viewtx,
+                       &(const V3D_SmoothParams) {.ofs = new_ofs, .dist = ok_dist ? &new_dist : NULL});
        }
 
        /* smooth view does viewlock RV3D_BOXVIEW copy */
@@ -3001,6 +3064,8 @@ static int viewselected_exec(bContext *C, wmOperator *op)
        ARegion *ar = CTX_wm_region(C);
        View3D *v3d = CTX_wm_view3d(C);
        Scene *scene = CTX_data_scene(C);
+       bGPdata *gpd = CTX_data_gpencil_data(C);
+       const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE));
        Object *ob = OBACT;
        Object *obedit = CTX_data_edit_object(C);
        float min[3], max[3];
@@ -3013,6 +3078,10 @@ static int viewselected_exec(bContext *C, wmOperator *op)
 
        INIT_MINMAX(min, max);
 
+       if (is_gp_edit) {
+               ob = NULL;
+       }
+
        if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT)) {
                /* hard-coded exception, we look for the one selected armature */
                /* this is weak code this way, we should make a generic active/selection callback interface once... */
@@ -3029,7 +3098,19 @@ static int viewselected_exec(bContext *C, wmOperator *op)
        }
 
 
-       if (obedit) {
+       if (is_gp_edit) {
+               CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+               {
+                       /* we're only interested in selected points here... */
+                       if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) {
+                               if (ED_gpencil_stroke_minmax(gps, true, min, max)) {
+                                       ok = true;
+                               }
+                       }
+               }
+               CTX_DATA_END;
+       }
+       else if (obedit) {
                ok = ED_view3d_minmax_verts(obedit, min, max);    /* only selected */
        }
        else if (ob && (ob->mode & OB_MODE_POSE)) {
@@ -3041,7 +3122,9 @@ static int viewselected_exec(bContext *C, wmOperator *op)
        else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
                ok = PE_minmax(scene, min, max);
        }
-       else if (ob && (ob->mode & (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT))) {
+       else if (ob &&
+                (ob->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)))
+       {
                BKE_paint_stroke_get_average(scene, ob, min);
                copy_v3_v3(max, min);
                ok = true;
@@ -3076,8 +3159,6 @@ static int viewselected_exec(bContext *C, wmOperator *op)
                view3d_from_minmax(C, v3d, ar, min, max, ok_dist, smooth_viewtx);
        }
 
-// XXX BIF_view3d_previewrender_signal(curarea, PR_DBASE|PR_DISPRECT);
-
        return OPERATOR_FINISHED;
 }
 
@@ -3195,13 +3276,15 @@ static int viewcenter_cursor_exec(bContext *C, wmOperator *op)
                ARegion *ar = CTX_wm_region(C);
                const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
 
+               ED_view3d_smooth_view_force_finish(C, v3d, ar);
+
                /* non camera center */
                float new_ofs[3];
                negate_v3_v3(new_ofs, ED_view3d_cursor3d_get(scene, v3d));
-               ED_view3d_smooth_view(C, v3d, ar, NULL, NULL,
-                                     new_ofs, NULL, NULL, NULL,
-                                     smooth_viewtx);
-               
+               ED_view3d_smooth_view(
+                       C, v3d, ar, smooth_viewtx,
+                       &(const V3D_SmoothParams) {.ofs = new_ofs});
+
                /* smooth view does viewlock RV3D_BOXVIEW copy */
        }
        
@@ -3234,6 +3317,8 @@ static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *ev
                float new_ofs[3];
                const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
 
+               ED_view3d_smooth_view_force_finish(C, v3d, ar);
+
                view3d_operator_needs_opengl(C);
 
                if (ED_view3d_autodist(scene, ar, v3d, event->mval, new_ofs, false, NULL)) {
@@ -3242,12 +3327,12 @@ static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *ev
                else {
                        /* fallback to simple pan */
                        negate_v3_v3(new_ofs, rv3d->ofs);
-                       ED_view3d_win_to_3d_int(ar, new_ofs, event->mval, new_ofs);
+                       ED_view3d_win_to_3d_int(v3d, ar, new_ofs, event->mval, new_ofs);
                }
                negate_v3(new_ofs);
-               ED_view3d_smooth_view(C, v3d, ar, NULL, NULL,
-                                     new_ofs, NULL, NULL, NULL,
-                                     smooth_viewtx);
+               ED_view3d_smooth_view(
+                       C, v3d, ar, smooth_viewtx,
+                       &(const V3D_SmoothParams) {.ofs = new_ofs});
        }
 
        return OPERATOR_FINISHED;
@@ -3394,12 +3479,8 @@ static int render_border_exec(bContext *C, wmOperator *op)
                WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
        }
 
-       /* drawing a border surrounding the entire camera view switches off border rendering
-        * or the border covers no pixels */
-       if ((border.xmin <= 0.0f && border.xmax >= 1.0f &&
-            border.ymin <= 0.0f && border.ymax >= 1.0f) ||
-           (border.xmin == border.xmax || border.ymin == border.ymax))
-       {
+       /* drawing a border outside the camera view switches off border rendering */
+       if ((border.xmin == border.xmax || border.ymin == border.ymax)) {
                if (rv3d->persp == RV3D_CAMOB)
                        scene->r.mode &= ~R_BORDER;
                else
@@ -3426,10 +3507,10 @@ void VIEW3D_OT_render_border(wmOperatorType *ot)
        ot->idname = "VIEW3D_OT_render_border";
 
        /* api callbacks */
-       ot->invoke = WM_border_select_invoke;
+       ot->invoke = WM_gesture_border_invoke;
        ot->exec = render_border_exec;
-       ot->modal = WM_border_select_modal;
-       ot->cancel = WM_border_select_cancel;
+       ot->modal = WM_gesture_border_modal;
+       ot->cancel = WM_gesture_border_cancel;
 
        ot->poll = ED_operator_view3d_active;
 
@@ -3499,7 +3580,6 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
        View3D *v3d = CTX_wm_view3d(C);
        RegionView3D *rv3d = CTX_wm_region_view3d(C);
        Scene *scene = CTX_data_scene(C);
-       int gesture_mode;
        const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
 
        /* Zooms in on a border drawn by the user */
@@ -3523,7 +3603,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
        WM_operator_properties_border_to_rcti(op, &rect);
 
        /* check if zooming in/out view */
-       gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
+       const bool zoom_in = !RNA_boolean_get(op->ptr, "zoom_out");
 
        ED_view3d_dist_range_get(v3d, dist_range);
 
@@ -3580,7 +3660,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
                dist_range[0] = v3d->near * 1.5f;
        }
        else { /* othographic */
-                  /* find the current window width and height */
+               /* find the current window width and height */
                vb[0] = ar->winx;
                vb[1] = ar->winy;
 
@@ -3621,7 +3701,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
                new_dist *= max_ff(xscale, yscale);
        }
 
-       if (gesture_mode == GESTURE_MODAL_OUT) {
+       if (!zoom_in) {
                sub_v3_v3v3(dvec, new_ofs, rv3d->ofs);
                new_dist = rv3d->dist * (rv3d->dist / new_dist);
                add_v3_v3v3(new_ofs, rv3d->ofs, dvec);
@@ -3630,9 +3710,9 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
        /* clamp after because we may have been zooming out */
        CLAMP(new_dist, dist_range[0], dist_range[1]);
 
-       ED_view3d_smooth_view(C, v3d, ar, NULL, NULL,
-                             new_ofs, NULL, &new_dist, NULL,
-                             smooth_viewtx);
+       ED_view3d_smooth_view(
+               C, v3d, ar, smooth_viewtx,
+               &(const V3D_SmoothParams) {.ofs = new_ofs, .dist = &new_dist});
 
        if (rv3d->viewlock & RV3D_BOXVIEW)
                view3d_boxview_sync(CTX_wm_area(C), ar);
@@ -3647,7 +3727,7 @@ static int view3d_zoom_border_invoke(bContext *C, wmOperator *op, const wmEvent
 
        /* if in camera view do not exec the operator so we do not conflict with set render border*/
        if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d))
-               return WM_border_select_invoke(C, op, event);
+               return WM_gesture_border_invoke(C, op, event);
        else
                return OPERATOR_PASS_THROUGH;
 }
@@ -3662,8 +3742,8 @@ void VIEW3D_OT_zoom_border(wmOperatorType *ot)
        /* api callbacks */
        ot->invoke = view3d_zoom_border_invoke;
        ot->exec = view3d_zoom_border_exec;
-       ot->modal = WM_border_select_modal;
-       ot->cancel = WM_border_select_cancel;
+       ot->modal = WM_gesture_border_modal;
+       ot->cancel = WM_gesture_border_cancel;
 
        ot->poll = ED_operator_region_view3d_active;
 
@@ -3671,7 +3751,7 @@ void VIEW3D_OT_zoom_border(wmOperatorType *ot)
        ot->flag = 0;
 
        /* rna */
-       WM_operator_properties_gesture_border(ot, false);
+       WM_operator_properties_gesture_border_zoom(ot);
 }
 
 /* sets the view to 1:1 camera/render-pixel */
@@ -3759,7 +3839,7 @@ static void axis_set_view(bContext *C, View3D *v3d, ARegion *ar,
                        float twmat[3][3];
 
                        /* same as transform manipulator when normal is set */
-                       ED_getTransformOrientationMatrix(C, twmat, V3D_ACTIVE);
+                       ED_getTransformOrientationMatrix(C, twmat, V3D_AROUND_ACTIVE);
 
                        mat3_to_quat(obact_quat, twmat);
                        invert_qt_normalized(obact_quat);
@@ -3787,9 +3867,9 @@ static void axis_set_view(bContext *C, View3D *v3d, ARegion *ar,
 
        if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
                /* to camera */
-               ED_view3d_smooth_view(C, v3d, ar, v3d->camera, NULL,
-                                     rv3d->ofs, quat, NULL, NULL,
-                                     smooth_viewtx);
+               ED_view3d_smooth_view(
+                       C, v3d, ar, smooth_viewtx,
+                       &(const V3D_SmoothParams) {.camera_old = v3d->camera, .ofs = rv3d->ofs, .quat = quat});
        }
        else if (orig_persp == RV3D_CAMOB && v3d->camera) {
                /* from camera */
@@ -3801,15 +3881,26 @@ static void axis_set_view(bContext *C, View3D *v3d, ARegion *ar,
                /* so we animate _from_ the camera location */
                ED_view3d_from_object(v3d->camera, rv3d->ofs, NULL, &rv3d->dist, NULL);
 
-               ED_view3d_smooth_view(C, v3d, ar, NULL, NULL,
-                                     ofs, quat, &dist, NULL,
-                                     smooth_viewtx);
+               ED_view3d_smooth_view(
+                       C, v3d, ar, smooth_viewtx,
+                       &(const V3D_SmoothParams) {.ofs = ofs, .quat = quat, .dist = &dist});
        }
        else {
+               /* rotate around selection */
+               const float *dyn_ofs_pt = NULL;
+               float dyn_ofs[3];
+
+               if (U.uiflag & USER_ORBIT_SELECTION) {
+                       if (view3d_orbit_calc_center(C, dyn_ofs)) {
+                               negate_v3(dyn_ofs);
+                               dyn_ofs_pt = dyn_ofs;
+                       }
+               }
+
                /* no camera involved */
-               ED_view3d_smooth_view(C, v3d, ar, NULL, NULL,
-                                     NULL, quat, NULL, NULL,
-                                     smooth_viewtx);
+               ED_view3d_smooth_view(
+                       C, v3d, ar, smooth_viewtx,
+                       &(const V3D_SmoothParams) {.quat = quat, .dyn_ofs = dyn_ofs_pt});
        }
 }
 
@@ -3828,6 +3919,8 @@ static int viewnumpad_exec(bContext *C, wmOperator *op)
        ED_view3d_context_user_region(C, &v3d, &ar);
        rv3d = ar->regiondata;
 
+       ED_view3d_smooth_view_force_finish(C, v3d, ar);
+
        viewnum = RNA_enum_get(op->ptr, "type");
        align_active = RNA_boolean_get(op->ptr, "align_active");
 
@@ -3898,9 +3991,11 @@ static int viewnumpad_exec(bContext *C, wmOperator *op)
 
                                /* finally do snazzy view zooming */
                                rv3d->persp = RV3D_CAMOB;
-                               ED_view3d_smooth_view(C, v3d, ar, NULL, v3d->camera,
-                                                     rv3d->ofs, rv3d->viewquat, &rv3d->dist, &v3d->lens,
-                                                     smooth_viewtx);
+                               ED_view3d_smooth_view(
+                                       C, v3d, ar, smooth_viewtx,
+                                       &(const V3D_SmoothParams) {
+                                           .camera = v3d->camera, .ofs = rv3d->ofs, .quat = rv3d->viewquat,
+                                           .dist = &rv3d->dist, .lens = &v3d->lens});
 
                        }
                        else {
@@ -3976,13 +4071,13 @@ static int vieworbit_exec(bContext *C, wmOperator *op)
                rv3d = ar->regiondata;
        }
 
+       ED_view3d_smooth_view_force_finish(C, v3d, ar);
+
        if ((rv3d->viewlock & RV3D_LOCKED) == 0 || (view_opposite != RV3D_VIEW_USER)) {
                if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
                        int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
                        float quat_mul[4];
                        float quat_new[4];
-                       float ofs_new[3];
-                       float *ofs_new_pt = NULL;
 
                        if (view_opposite == RV3D_VIEW_USER) {
                                view3d_ensure_persp(v3d, ar);
@@ -4008,6 +4103,9 @@ static int vieworbit_exec(bContext *C, wmOperator *op)
 
                        mul_qt_qtqt(quat_new, rv3d->viewquat, quat_mul);
 
+                       /* avoid precision loss over time */
+                       normalize_qt(quat_new);
+
                        if (view_opposite != RV3D_VIEW_USER) {
                                rv3d->view = view_opposite;
                                /* avoid float in-precision, just get a new orientation */
@@ -4017,25 +4115,19 @@ static int vieworbit_exec(bContext *C, wmOperator *op)
                                rv3d->view = RV3D_VIEW_USER;
                        }
 
-                       if (U.uiflag & USER_ORBIT_SELECTION) {
-                               float dyn_ofs[3];
-
-                               view3d_orbit_calc_center(C, dyn_ofs);
-                               negate_v3(dyn_ofs);
-
-                               copy_v3_v3(ofs_new, rv3d->ofs);
 
-                               view3d_orbit_apply_dyn_ofs(ofs_new, dyn_ofs, rv3d->viewquat, quat_new);
-                               ofs_new_pt = ofs_new;
+                       float dyn_ofs[3], *dyn_ofs_pt = NULL;
 
-                               /* disable smoothview in this case
-                                * although it works OK, it looks a little odd. */
-                               smooth_viewtx = 0;
+                       if (U.uiflag & USER_ORBIT_SELECTION) {
+                               if (view3d_orbit_calc_center(C, dyn_ofs)) {
+                                       negate_v3(dyn_ofs);
+                                       dyn_ofs_pt = dyn_ofs;
+                               }
                        }
 
-                       ED_view3d_smooth_view(C, v3d, ar, NULL, NULL,
-                                             ofs_new_pt, quat_new, NULL, NULL,
-                                             smooth_viewtx);
+                       ED_view3d_smooth_view(
+                               C, v3d, ar, smooth_viewtx,
+                               &(const V3D_SmoothParams) {.quat = quat_new, .dyn_ofs = dyn_ofs_pt});
 
                        return OPERATOR_FINISHED;
                }
@@ -4080,6 +4172,10 @@ static void view_roll_angle(ARegion *ar, float quat[4], const float orig_quat[4]
        axis_angle_normalized_to_quat(quat_mul, dvec, angle);
 
        mul_qt_qtqt(quat, orig_quat, quat_mul);
+
+       /* avoid precision loss over time */
+       normalize_qt(quat);
+
        rv3d->view = RV3D_VIEW_USER;
 }
 
@@ -4099,6 +4195,10 @@ static void viewroll_apply(ViewOpsData *vod, int x, int UNUSED(y))
        if (angle != 0.0f)
                view_roll_angle(vod->ar, vod->rv3d->viewquat, vod->oldquat, vod->mousevec, angle);
 
+       if (vod->use_dyn_ofs) {
+               view3d_orbit_apply_dyn_ofs(vod->rv3d->ofs, vod->ofs, vod->oldquat, vod->rv3d->viewquat, vod->dyn_ofs);
+       }
+
        if (vod->rv3d->viewlock & RV3D_BOXVIEW)
                view3d_boxview_sync(vod->sa, vod->ar);
 
@@ -4161,9 +4261,9 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event)
 }
 
 static EnumPropertyItem prop_view_roll_items[] = {
-       {0, "ROLLANGLE", 0, "Roll Angle", "Roll the view using an angle value"},
-       {V3D_VIEW_STEPLEFT, "ROLLLEFT", 0, "Roll Left", "Roll the view around to the Left"},
-       {V3D_VIEW_STEPRIGHT, "ROLLTRIGHT", 0, "Roll Right", "Roll the view around to the Right"},
+       {0, "ANGLE", 0, "Roll Angle", "Roll the view using an angle value"},
+       {V3D_VIEW_STEPLEFT, "LEFT", 0, "Roll Left", "Roll the view around to the Left"},
+       {V3D_VIEW_STEPRIGHT, "RIGHT", 0, "Roll Right", "Roll the view around to the Right"},
        {0, NULL, 0, NULL, NULL}
 };
 
@@ -4185,6 +4285,9 @@ static int viewroll_exec(bContext *C, wmOperator *op)
 
        rv3d = ar->regiondata;
        if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
+
+               ED_view3d_smooth_view_force_finish(C, v3d, ar);
+
                int type = RNA_enum_get(op->ptr, "type");
                float angle = (type == 0) ? RNA_float_get(op->ptr, "angle") : DEG2RADF(U.pad_rot_angle);
                float mousevec[3];
@@ -4200,9 +4303,18 @@ static int viewroll_exec(bContext *C, wmOperator *op)
                negate_v3(mousevec);
                view_roll_angle(ar, quat_new, rv3d->viewquat, mousevec, angle);
 
-               ED_view3d_smooth_view(C, v3d, ar, NULL, NULL,
-                                     NULL, quat_new, NULL, NULL,
-                                     smooth_viewtx);
+               const float *dyn_ofs_pt = NULL;
+               float dyn_ofs[3];
+               if (U.uiflag & USER_ORBIT_SELECTION) {
+                       if (view3d_orbit_calc_center(C, dyn_ofs)) {
+                               negate_v3(dyn_ofs);
+                               dyn_ofs_pt = dyn_ofs;
+                       }
+               }
+
+               ED_view3d_smooth_view(
+                       C, v3d, ar, smooth_viewtx,
+                       &(const V3D_SmoothParams) {.quat = quat_new, .dyn_ofs = dyn_ofs_pt});
 
                viewops_data_free(C, op);
                return OPERATOR_FINISHED;
@@ -4225,9 +4337,11 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
        else {
                /* makes op->customdata */
                viewops_data_alloc(C, op);
-               viewops_data_create(C, op, event);
+               viewops_data_create(C, op, event, false);
                vod = op->customdata;
 
+               ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->ar);
+
                /* overwrite the mouse vector with the view direction */
                normalize_v3_v3(vod->mousevec, vod->rv3d->viewinv[2]);
                negate_v3(vod->mousevec);
@@ -4289,41 +4403,24 @@ static EnumPropertyItem prop_view_pan_items[] = {
        {0, NULL, 0, NULL, NULL}
 };
 
-static int viewpan_exec(bContext *C, wmOperator *op)
+static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
-       ScrArea *sa = CTX_wm_area(C);
-       ARegion *ar = CTX_wm_region(C);
-       View3D *v3d = CTX_wm_view3d(C);
-       RegionView3D *rv3d = CTX_wm_region_view3d(C);
-       float vec[3];
-       const float co_zero[3] = {0.0f};
-       float mval_f[2] = {0.0f, 0.0f};
-       float zfac;
-       int pandir;
-
-       if (view3d_operator_offset_lock_check(C, op))
-               return OPERATOR_CANCELLED;
-
-       pandir = RNA_enum_get(op->ptr, "type");
-
-       ED_view3d_camera_lock_init(v3d, rv3d);
+       int x = 0, y = 0;
+       int pandir = RNA_enum_get(op->ptr, "type");
 
-       zfac = ED_view3d_calc_zfac(rv3d, co_zero, NULL);
-       if      (pandir == V3D_VIEW_PANRIGHT)  { mval_f[0] = -32.0f; }
-       else if (pandir == V3D_VIEW_PANLEFT)   { mval_f[0] =  32.0f; }
-       else if (pandir == V3D_VIEW_PANUP)     { mval_f[1] = -25.0f; }
-       else if (pandir == V3D_VIEW_PANDOWN)   { mval_f[1] =  25.0f; }
-       ED_view3d_win_to_delta(ar, mval_f, vec, zfac);
-       add_v3_v3(rv3d->ofs, vec);
-
-       if (rv3d->viewlock & RV3D_BOXVIEW)
-               view3d_boxview_sync(sa, ar);
+       if      (pandir == V3D_VIEW_PANRIGHT)  { x = -32; }
+       else if (pandir == V3D_VIEW_PANLEFT)   { x =  32; }
+       else if (pandir == V3D_VIEW_PANUP)     { y = -25; }
+       else if (pandir == V3D_VIEW_PANDOWN)   { y =  25; }
 
-       ED_view3d_depth_tag_update(rv3d);
+       viewops_data_alloc(C, op);
+       viewops_data_create(C, op, event, false);
+       ViewOpsData *vod = op->customdata;
 
-       ED_view3d_camera_lock_sync(v3d, rv3d);
+       viewmove_apply(vod, vod->oldx + x, vod->oldy + y);
 
-       ED_region_tag_redraw(ar);
+       ED_view3d_depth_tag_update(vod->rv3d);
+       viewops_data_free(C, op);
 
        return OPERATOR_FINISHED;
 }
@@ -4336,7 +4433,7 @@ void VIEW3D_OT_view_pan(wmOperatorType *ot)
        ot->idname = "VIEW3D_OT_view_pan";
 
        /* api callbacks */
-       ot->exec = viewpan_exec;
+       ot->invoke = viewpan_invoke;
        ot->poll = ED_operator_region_view3d_active;
 
        /* flags */
@@ -4453,7 +4550,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot)
        /* note: having key shortcut here is bad practice,
         * but for now keep because this displays when dragging an image over the 3D viewport */
        ot->name   = "Add Background Image (Ctrl for Empty Object)";
-       ot->description = "Add a new background image";
+       ot->description = "Add a new background image (Ctrl for Empty Object)";
        ot->idname = "VIEW3D_OT_background_image_add";
 
        /* api callbacks */
@@ -4462,12 +4559,13 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot)
        ot->poll   = ED_operator_view3d_active;
 
        /* flags */
-       ot->flag   = 0;
+       ot->flag   = OPTYPE_UNDO;
        
        /* properties */
        RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign");
-       WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, FILE_OPENFILE,
-                                      WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+       WM_operator_properties_filesel(
+               ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, FILE_OPENFILE,
+               WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
 }
 
 
@@ -4573,7 +4671,7 @@ static int view3d_clipping_invoke(bContext *C, wmOperator *op, const wmEvent *ev
                return OPERATOR_FINISHED;
        }
        else {
-               return WM_border_select_invoke(C, op, event);
+               return WM_gesture_border_invoke(C, op, event);
        }
 }
 
@@ -4589,8 +4687,8 @@ void VIEW3D_OT_clip_border(wmOperatorType *ot)
        /* api callbacks */
        ot->invoke = view3d_clipping_invoke;
        ot->exec = view3d_clipping_exec;
-       ot->modal = WM_border_select_modal;
-       ot->cancel = WM_border_select_cancel;
+       ot->modal = WM_gesture_border_modal;
+       ot->cancel = WM_gesture_border_cancel;
 
        ot->poll = ED_operator_region_view3d_active;
 
@@ -4637,7 +4735,7 @@ void ED_view3d_cursor3d_position(bContext *C, float fp[3], const int mval[2])
        if (depth_used == false) {
                float depth_pt[3];
                copy_v3_v3(depth_pt, fp);
-               ED_view3d_win_to_3d_int(ar, depth_pt, mval, fp);
+               ED_view3d_win_to_3d_int(v3d, ar, depth_pt, mval, fp);
        }
 }
 
@@ -4658,13 +4756,20 @@ void ED_view3d_cursor3d_update(bContext *C, const int mval[2])
                ARegion *ar = CTX_wm_region(C);
                RegionView3D *rv3d = ar->regiondata;
 
-               float co_curr[2], co_prev[2];
+               if (U.uiflag & USER_LOCK_CURSOR_ADJUST) {
 
-               if ((ED_view3d_project_float_global(ar, fp_prev, co_prev, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) &&
-                   (ED_view3d_project_float_global(ar, fp_curr, co_curr, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK))
-               {
-                       rv3d->ofs_lock[0] += (co_curr[0] - co_prev[0]) / (ar->winx * 0.5f);
-                       rv3d->ofs_lock[1] += (co_curr[1] - co_prev[1]) / (ar->winy * 0.5f);
+                       float co_curr[2], co_prev[2];
+
+                       if ((ED_view3d_project_float_global(ar, fp_prev, co_prev, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) &&
+                           (ED_view3d_project_float_global(ar, fp_curr, co_curr, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK))
+                       {
+                               rv3d->ofs_lock[0] += (co_curr[0] - co_prev[0]) / (ar->winx * 0.5f);
+                               rv3d->ofs_lock[1] += (co_curr[1] - co_prev[1]) / (ar->winy * 0.5f);
+                       }
+               }
+               else {
+                       /* Cursor may be outside of the view, prevent it getting 'lost', see: T40353 & T45301 */
+                       zero_v2(rv3d->ofs_lock);
                }
        }
 
@@ -4711,13 +4816,10 @@ static int manipulator_invoke(bContext *C, wmOperator *op, const wmEvent *event)
        if (!(v3d->twflag & V3D_USE_MANIPULATOR)) return OPERATOR_PASS_THROUGH;
        if (!(v3d->twflag & V3D_DRAW_MANIPULATOR)) return OPERATOR_PASS_THROUGH;
 
-       /* only no modifier or shift */
-       if (event->keymodifier != 0 && event->keymodifier != KM_SHIFT) return OPERATOR_PASS_THROUGH;
-
        /* note; otherwise opengl won't work */
        view3d_operator_needs_opengl(C);
 
-       if (0 == BIF_do_manipulator(C, event, op))
+       if (BIF_do_manipulator(C, event, op) == 0)
                return OPERATOR_PASS_THROUGH;
 
        return OPERATOR_FINISHED;
@@ -4725,6 +4827,7 @@ static int manipulator_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 
 void VIEW3D_OT_manipulator(wmOperatorType *ot)
 {
+       PropertyRNA *prop;
 
        /* identifiers */
        ot->name = "3D Manipulator";
@@ -4738,6 +4841,10 @@ void VIEW3D_OT_manipulator(wmOperatorType *ot)
 
        /* properties to pass to transform */
        Transform_Properties(ot, P_CONSTRAINT);
+
+       prop = RNA_def_boolean(ot->srna, "use_planar_constraint", false, "Planar Constraint", "Limit the transformation to the "
+                              "two axes that have not been clicked (translate/scale only)");
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
 }
 
 static int enable_manipulator_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
@@ -4780,6 +4887,35 @@ void VIEW3D_OT_enable_manipulator(wmOperatorType *ot)
        RNA_def_property_flag(prop, PROP_SKIP_SAVE);
 }
 
+/* ************************* Toggle rendered shading *********************** */
+
+static int toggle_render_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       View3D *v3d = CTX_wm_view3d(C);
+       if (v3d->drawtype == OB_RENDER) {
+               v3d->drawtype = v3d->prev_drawtype;
+       }
+       else {
+               v3d->prev_drawtype = v3d->drawtype;
+               v3d->drawtype = OB_RENDER;
+       }
+       ED_view3d_shade_update(CTX_data_main(C), CTX_data_scene(C), v3d, CTX_wm_area(C));
+       WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+       return OPERATOR_FINISHED;
+}
+
+void VIEW3D_OT_toggle_render(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Toggle Rendered Shading";
+       ot->description = "Toggle rendered shading mode of the viewport";
+       ot->idname = "VIEW3D_OT_toggle_render";
+
+       /* api callbacks */
+       ot->exec = toggle_render_exec;
+       ot->poll = ED_operator_view3d_active;
+}
+
 /* ************************* below the line! *********************** */
 
 
@@ -4797,11 +4933,7 @@ static float view_autodist_depth_margin(ARegion *ar, const int mval[2], int marg
                rect.ymax = mval[1] + 1;
        }
        else {
-               rect.xmax = mval[0] + margin;
-               rect.ymax = mval[1] + margin;
-
-               rect.xmin = mval[0] - margin;
-               rect.ymin = mval[1] - margin;
+               BLI_rcti_init_pt_radius(&rect, mval, margin);
        }
 
        view3d_update_depths_rect(ar, &depth_temp, &rect);
@@ -4814,7 +4946,7 @@ static float view_autodist_depth_margin(ARegion *ar, const int mval[2], int marg
  * Get the world-space 3d location from a screen-space 2d point.
  *
  * \param mval: Input screen-space pixel location.
- * \param mouse_worldloc: Output world-space loction.
+ * \param mouse_worldloc: Output world-space location.
  * \param fallback_depth_pt: Use this points depth when no depth can be found.
  */
 bool ED_view3d_autodist(
@@ -4830,9 +4962,11 @@ bool ED_view3d_autodist(
        bool depth_ok = false;
 
        /* Get Z Depths, needed for perspective, nice for ortho */
-       bgl_get_mats(&mats);
        ED_view3d_draw_depth(scene, ar, v3d, alphaoverride);
 
+       /* call after in case settings have been modified since last drawing, see: T47089 */
+       bgl_get_mats(&mats);
+
        /* Attempt with low margin's first */
        i = 0;
        do {
@@ -4855,7 +4989,7 @@ bool ED_view3d_autodist(
        }
 
        if (fallback_depth_pt) {
-               ED_view3d_win_to_3d_int(ar, fallback_depth_pt, mval, mouse_worldloc);
+               ED_view3d_win_to_3d_int(v3d, ar, fallback_depth_pt, mval, mouse_worldloc);
                return true;
        }
        else {
@@ -4951,7 +5085,7 @@ bool ED_view3d_autodist_depth_seg(ARegion *ar, const int mval_sta[2], const int
        copy_v2_v2_int(p1, mval_sta);
        copy_v2_v2_int(p2, mval_end);
 
-       plot_line_v2v2i(p1, p2, depth_segment_cb, &data);
+       BLI_bitmap_draw_2d_line_v2v2i(p1, p2, depth_segment_cb, &data);
 
        *depth = data.depth;