Cleanup: remove moar G.main usages.
[blender.git] / source / blender / editors / transform / transform_conversions.c
index 5deb5ee96351fe6043640cc1ba1f392bf326317c..6ce8d9dc5ac96e7a3bcc3f22a5d614ba9fb7d880 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <string.h>
 #include <math.h>
+#include <limits.h>
 
 #include "DNA_anim_types.h"
 #include "DNA_brush_types.h"
@@ -58,6 +59,7 @@
 #include "BLI_linklist_stack.h"
 #include "BLI_string.h"
 #include "BLI_bitmap.h"
+#include "BLI_rect.h"
 
 #include "BKE_DerivedMesh.h"
 #include "BKE_action.h"
 
 #include "RNA_access.h"
 
+#include "DEG_depsgraph.h"
+
 #include "transform.h"
 #include "bmesh.h"
 
 static void transform_around_single_fallback(TransInfo *t)
 {
        if ((t->total == 1) &&
-           (ELEM(t->around, V3D_CENTER, V3D_CENTROID, V3D_ACTIVE)) &&
+           (ELEM(t->around, V3D_AROUND_CENTER_BOUNDS, V3D_AROUND_CENTER_MEAN, V3D_AROUND_ACTIVE)) &&
            (ELEM(t->mode, TFM_RESIZE, TFM_ROTATION, TFM_TRACKBALL)))
        {
-               t->around = V3D_LOCAL;
+               t->around = V3D_AROUND_LOCAL_ORIGINS;
        }
 }
 
@@ -170,7 +174,7 @@ void sort_trans_data_dist(TransInfo *t)
 
        for (i = 0; i < t->total && start->flag & TD_SELECTED; i++)
                start++;
-       
+
        if (i < t->total) {
                if (t->flag & T_PROP_CONNECTED)
                        qsort(start, t->total - i, sizeof(TransData), trans_data_compare_dist);
@@ -340,20 +344,20 @@ static void createTransEdge(TransInfo *t)
        BMIter iter;
        float mtx[3][3], smtx[3][3];
        int count = 0, countsel = 0;
-       int propmode = t->flag & T_PROP_EDIT;
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
        int cd_edge_float_offset;
 
        BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
                if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
                        if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) countsel++;
-                       if (propmode) count++;
+                       if (is_prop_edit) count++;
                }
        }
 
        if (countsel == 0)
                return;
 
-       if (propmode) {
+       if (is_prop_edit) {
                t->total = count;
        }
        else {
@@ -379,7 +383,7 @@ static void createTransEdge(TransInfo *t)
        BLI_assert(cd_edge_float_offset != -1);
 
        BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
-               if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && (BM_elem_flag_test(eed, BM_ELEM_SELECT) || propmode)) {
+               if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && (BM_elem_flag_test(eed, BM_ELEM_SELECT) || is_prop_edit)) {
                        float *fl_ptr;
                        /* need to set center for center calculations */
                        mid_v3_v3v3(td->center, eed->v1->co, eed->v2->co);
@@ -471,9 +475,9 @@ static short apply_targetless_ik(Object *ob)
                                /* apply and decompose, doesn't work for constraints or non-uniform scale well */
                                {
                                        float rmat3[3][3], qrmat[3][3], imat3[3][3], smat[3][3];
-                                       
+
                                        copy_m3_m4(rmat3, rmat);
-                                       
+
                                        /* rotation */
                                        /* [#22409] is partially caused by this, as slight numeric error introduced during
                                         * the solving process leads to locked-axis values changing. However, we cannot modify
@@ -486,7 +490,7 @@ static short apply_targetless_ik(Object *ob)
                                                mat3_to_axis_angle(parchan->rotAxis, &parchan->rotAngle, rmat3);
                                        else
                                                mat3_to_quat(parchan->quat, rmat3);
-                                       
+
                                        /* for size, remove rotation */
                                        /* causes problems with some constraints (so apply only if needed) */
                                        if (data->flag & CONSTRAINT_IK_STRETCH) {
@@ -496,12 +500,12 @@ static short apply_targetless_ik(Object *ob)
                                                        axis_angle_to_mat3(qrmat, parchan->rotAxis, parchan->rotAngle);
                                                else
                                                        quat_to_mat3(qrmat, parchan->quat);
-                                               
+
                                                invert_m3_m3(imat3, qrmat);
                                                mul_m3_m3m3(smat, rmat3, imat3);
                                                mat3_to_size(parchan->size, smat);
                                        }
-                                       
+
                                        /* causes problems with some constraints (e.g. childof), so disable this */
                                        /* as it is IK shouldn't affect location directly */
                                        /* copy_v3_v3(parchan->loc, rmat[3]); */
@@ -551,7 +555,7 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr
                td->ext->rotAxis = NULL;
                td->ext->rotAngle = NULL;
                td->ext->quat = NULL;
-               
+
                copy_v3_v3(td->ext->irot, pchan->eul);
        }
        else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
@@ -559,7 +563,7 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr
                td->ext->rotAxis = pchan->rotAxis;
                td->ext->rotAngle = &pchan->rotAngle;
                td->ext->quat = NULL;
-               
+
                td->ext->irotAngle = pchan->rotAngle;
                copy_v3_v3(td->ext->irotAxis, pchan->rotAxis);
        }
@@ -568,7 +572,7 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr
                td->ext->rotAxis = NULL;
                td->ext->rotAngle = NULL;
                td->ext->quat = pchan->quat;
-               
+
                copy_qt_qt(td->ext->iquat, pchan->quat);
        }
        td->ext->rotOrder = pchan->rotmode;
@@ -621,16 +625,16 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr
                        td->flag |= TD_PBONE_LOCAL_MTX_P;
                }
        }
-       
+
        /* for axismat we use bone's own transform */
        copy_m3_m4(pmat, pchan->pose_mat);
        mul_m3_m3m3(td->axismtx, omat, pmat);
        normalize_m3(td->axismtx);
 
-       if (t->mode == TFM_BONESIZE) {
+       if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
                bArmature *arm = t->poseobj->data;
 
-               if (arm->drawtype == ARM_ENVELOPE) {
+               if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) {
                        td->loc = NULL;
                        td->val = &bone->dist;
                        td->ival = bone->dist;
@@ -677,7 +681,7 @@ static void bone_children_clear_transflag(int mode, short around, ListBase *lb)
                }
                else if ((bone->flag & BONE_TRANSFORM) &&
                         (mode == TFM_ROTATION || mode == TFM_TRACKBALL) &&
-                        (around == V3D_LOCAL))
+                        (around == V3D_AROUND_LOCAL_ORIGINS))
                {
                        bone->flag |= BONE_TRANSFORM_CHILD;
                }
@@ -697,7 +701,6 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
        bPoseChannel *pchan;
        Bone *bone;
        int mode = *out_mode;
-       int hastranslation = 0;
        int total = 0;
 
        for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
@@ -707,7 +710,7 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
                                bone->flag |= BONE_TRANSFORM;
                        else
                                bone->flag &= ~BONE_TRANSFORM;
-                       
+
                        bone->flag &= ~BONE_HINGE_CHILD_TRANSFORM;
                        bone->flag &= ~BONE_TRANSFORM_CHILD;
                }
@@ -717,7 +720,7 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
 
        /* make sure no bone can be transformed when a parent is transformed */
        /* since pchans are depsgraph sorted, the parents are in beginning of list */
-       if (mode != TFM_BONESIZE) {
+       if (!ELEM(mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
                for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
                        bone = pchan->bone;
                        if (bone->flag & BONE_TRANSFORM)
@@ -725,31 +728,40 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
                }
        }
        /* now count, and check if we have autoIK or have to switch from translate to rotate */
-       hastranslation = 0;
+       bool has_translation = false, has_rotation = false;
 
        for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
                bone = pchan->bone;
                if (bone->flag & BONE_TRANSFORM) {
                        total++;
-                       
+
                        if (mode == TFM_TRANSLATION) {
                                if (has_targetless_ik(pchan) == NULL) {
                                        if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) {
                                                if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM)
-                                                       hastranslation = 1;
+                                                       has_translation = true;
+                                       }
+                                       else {
+                                               if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC)
+                                                       has_translation = true;
                                        }
-                                       else if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC)
-                                               hastranslation = 1;
+                                       if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT)
+                                               has_rotation = true;
                                }
                                else
-                                       hastranslation = 1;
+                                       has_translation = true;
                        }
                }
        }
 
        /* if there are no translatable bones, do rotation */
-       if (mode == TFM_TRANSLATION && !hastranslation) {
-               *out_mode = TFM_ROTATION;
+       if (mode == TFM_TRANSLATION && !has_translation) {
+               if (has_rotation) {
+                       *out_mode = TFM_ROTATION;
+               }
+               else {
+                       *out_mode = TFM_RESIZE;
+               }
        }
 
        return total;
@@ -759,29 +771,37 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
 /* -------- Auto-IK ---------- */
 
 /* adjust pose-channel's auto-ik chainlen */
-static void pchan_autoik_adjust(bPoseChannel *pchan, short chainlen)
+static bool pchan_autoik_adjust(bPoseChannel *pchan, short chainlen)
 {
        bConstraint *con;
+       bool changed = false;
 
        /* don't bother to search if no valid constraints */
-       if ((pchan->constflag & (PCHAN_HAS_IK | PCHAN_HAS_TARGET)) == 0)
-               return;
+       if ((pchan->constflag & (PCHAN_HAS_IK | PCHAN_HAS_TARGET)) == 0) {
+               return changed;
+       }
 
        /* check if pchan has ik-constraint */
        for (con = pchan->constraints.first; con; con = con->next) {
                if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce != 0.0f)) {
                        bKinematicConstraint *data = con->data;
-                       
+
                        /* only accept if a temporary one (for auto-ik) */
                        if (data->flag & CONSTRAINT_IK_TEMP) {
                                /* chainlen is new chainlen, but is limited by maximum chainlen */
-                               if ((chainlen == 0) || (chainlen > data->max_rootbone))
+                               const int old_rootbone = data->rootbone;
+                               if ((chainlen == 0) || (chainlen > data->max_rootbone)) {
                                        data->rootbone = data->max_rootbone;
-                               else
+                               }
+                               else {
                                        data->rootbone = chainlen;
+                               }
+                               changed |= (data->rootbone != old_rootbone);
                        }
                }
        }
+
+       return changed;
 }
 
 /* change the chain-length of auto-ik */
@@ -797,7 +817,13 @@ void transform_autoik_update(TransInfo *t, short mode)
        }
        else if (mode == -1) {
                /* mode==-1 is from WHEELMOUSEUP... decreases len */
-               if (*chainlen > 0) (*chainlen)--;
+               if (*chainlen > 0) {
+                       (*chainlen)--;
+               }
+               else {
+                       /* IK length did not change, skip updates. */
+                       return;
+               }
        }
 
        /* sanity checks (don't assume t->poseobj is set, or that it is an armature) */
@@ -805,8 +831,19 @@ void transform_autoik_update(TransInfo *t, short mode)
                return;
 
        /* apply to all pose-channels */
+       bool changed = false;
        for (pchan = t->poseobj->pose->chanbase.first; pchan; pchan = pchan->next) {
-               pchan_autoik_adjust(pchan, *chainlen);
+               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(G.main);
+               }
        }
 }
 
@@ -816,13 +853,16 @@ static void pose_grab_with_ik_clear(Object *ob)
        bKinematicConstraint *data;
        bPoseChannel *pchan;
        bConstraint *con, *next;
+#ifdef WITH_LEGACY_DEPSGRAPH
+       bool need_dependency_update = false;
+#endif
 
        for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
                /* clear all temporary lock flags */
                pchan->ikflag &= ~(BONE_IK_NO_XDOF_TEMP | BONE_IK_NO_YDOF_TEMP | BONE_IK_NO_ZDOF_TEMP);
-               
+
                pchan->constflag &= ~(PCHAN_HAS_IK | PCHAN_HAS_TARGET);
-               
+
                /* remove all temporary IK-constraints added */
                for (con = pchan->constraints.first; con; con = next) {
                        next = con->next;
@@ -830,6 +870,9 @@ static void pose_grab_with_ik_clear(Object *ob)
                                data = con->data;
                                if (data->flag & CONSTRAINT_IK_TEMP) {
                                        /* iTaSC needs clear for removed constraints */
+#ifdef WITH_LEGACY_DEPSGRAPH
+                                       need_dependency_update = true;
+#endif
                                        BIK_clear_data(ob->pose);
 
                                        BLI_remlink(&pchan->constraints, con);
@@ -843,6 +886,14 @@ static void pose_grab_with_ik_clear(Object *ob)
                        }
                }
        }
+
+#ifdef WITH_LEGACY_DEPSGRAPH
+       if (!DEG_depsgraph_use_legacy() && need_dependency_update)
+#endif
+       {
+               /* TODO(sergey): Consider doing partial update only. */
+               DAG_relations_tag_update(G.main);
+       }
 }
 
 /* adds the IK to pchan - returns if added */
@@ -860,15 +911,15 @@ static short pose_grab_with_ik_add(bPoseChannel *pchan)
        for (con = pchan->constraints.first; con; con = con->next) {
                if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
                        data = con->data;
-                       
+
                        if (data->tar == NULL || (data->tar->type == OB_ARMATURE && data->subtarget[0] == '\0')) {
                                /* make reference to constraint to base things off later (if it's the last targetless constraint encountered) */
                                targetless = (bKinematicConstraint *)con->data;
-                               
+
                                /* but, if this is a targetless IK, we make it auto anyway (for the children loop) */
                                if (con->enforce != 0.0f) {
                                        data->flag |= CONSTRAINT_IK_AUTO;
-                                       
+
                                        /* if no chain length has been specified, just make things obey standard rotation locks too */
                                        if (data->rootbone == 0) {
                                                for (; pchan; pchan = pchan->parent) {
@@ -879,11 +930,11 @@ static short pose_grab_with_ik_add(bPoseChannel *pchan)
                                                        if (pchan->protectflag & OB_LOCK_ROTZ) pchan->ikflag |= BONE_IK_NO_ZDOF_TEMP;
                                                }
                                        }
-                                       
-                                       return 0; 
+
+                                       return 0;
                                }
                        }
-                       
+
                        if ((con->flag & CONSTRAINT_DISABLE) == 0 && (con->enforce != 0.0f))
                                return 0;
                }
@@ -901,7 +952,7 @@ static short pose_grab_with_ik_add(bPoseChannel *pchan)
        data->flag |= CONSTRAINT_IK_TEMP | CONSTRAINT_IK_AUTO | CONSTRAINT_IK_POS;
        copy_v3_v3(data->grabtarget, pchan->pose_tail);
        data->rootbone = 0; /* watch-it! has to be 0 here, since we're still on the same bone for the first time through the loop [#25885] */
-       
+
        /* we only include bones that are part of a continual connected chain */
        do {
                /* here, we set ik-settings for bone from pchan->protectflag */
@@ -909,10 +960,10 @@ static short pose_grab_with_ik_add(bPoseChannel *pchan)
                if (pchan->protectflag & OB_LOCK_ROTX) pchan->ikflag |= BONE_IK_NO_XDOF_TEMP;
                if (pchan->protectflag & OB_LOCK_ROTY) pchan->ikflag |= BONE_IK_NO_YDOF_TEMP;
                if (pchan->protectflag & OB_LOCK_ROTZ) pchan->ikflag |= BONE_IK_NO_ZDOF_TEMP;
-               
+
                /* now we count this pchan as being included */
                data->rootbone++;
-               
+
                /* continue to parent, but only if we're connected to it */
                if (pchan->bone->flag & BONE_CONNECTED)
                        pchan = pchan->parent;
@@ -993,8 +1044,16 @@ static short pose_grab_with_ik(Object *ob)
        }
 
        /* iTaSC needs clear for new IK constraints */
-       if (tot_ik)
+       if (tot_ik) {
                BIK_clear_data(ob->pose);
+#ifdef WITH_LEGACY_DEPSGRAPH
+               if (!DEG_depsgraph_use_legacy())
+#endif
+               {
+                       /* TODO(sergey): Consuder doing partial update only. */
+                       DAG_relations_tag_update(G.main);
+               }
+       }
 
        return (tot_ik) ? 1 : 0;
 }
@@ -1068,12 +1127,12 @@ static void createTransPose(TransInfo *t, Object *ob)
 void restoreBones(TransInfo *t)
 {
        bArmature *arm = t->obedit->data;
-       BoneInitData *bid = t->customData;
+       BoneInitData *bid = t->custom.type.data;
        EditBone *ebo;
 
        while (bid->bone) {
                ebo = bid->bone;
-               
+
                ebo->dist = bid->dist;
                ebo->rad_tail = bid->rad_tail;
                ebo->roll = bid->roll;
@@ -1081,7 +1140,7 @@ void restoreBones(TransInfo *t)
                ebo->zwidth = bid->zwidth;
                copy_v3_v3(ebo->head, bid->head);
                copy_v3_v3(ebo->tail, bid->tail);
-               
+
                if (arm->flag & ARM_MIRROR_EDIT) {
                        EditBone *ebo_child;
 
@@ -1100,7 +1159,7 @@ void restoreBones(TransInfo *t)
                                parent->rad_tail = ebo->rad_head;
                        }
                }
-               
+
                bid++;
        }
 }
@@ -1118,13 +1177,13 @@ static void createTransArmatureVerts(TransInfo *t)
        int total_mirrored = 0, i;
        int oldtot;
        BoneInitData *bid;
-       
+
        t->total = 0;
        for (ebo = edbo->first; ebo; ebo = ebo->next) {
                oldtot = t->total;
 
                if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) {
-                       if (t->mode == TFM_BONESIZE) {
+                       if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
                                if (ebo->flag & BONE_SELECTED)
                                        t->total++;
                        }
@@ -1141,7 +1200,7 @@ static void createTransArmatureVerts(TransInfo *t)
                }
 
                if (mirror && (oldtot < t->total)) {
-                       eboflip = ED_armature_bone_get_mirrored(arm->edbo, ebo);
+                       eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo);
                        if (eboflip)
                                total_mirrored++;
                }
@@ -1157,8 +1216,8 @@ static void createTransArmatureVerts(TransInfo *t)
        td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransEditBone");
 
        if (mirror) {
-               t->customData = bid = MEM_mallocN((total_mirrored + 1) * sizeof(BoneInitData), "BoneInitData");
-               t->flag |= T_FREE_CUSTOMDATA;
+               t->custom.type.data = bid = MEM_mallocN((total_mirrored + 1) * sizeof(BoneInitData), "BoneInitData");
+               t->custom.type.use_free = true;
        }
 
        i = 0;
@@ -1202,9 +1261,9 @@ static void createTransArmatureVerts(TransInfo *t)
                                }
 
                        }
-                       else if (t->mode == TFM_BONESIZE) {
+                       else if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) {
                                if (ebo->flag & BONE_SELECTED) {
-                                       if (arm->drawtype == ARM_ENVELOPE) {
+                                       if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) {
                                                td->loc = NULL;
                                                td->val = &ebo->dist;
                                                td->ival = ebo->dist;
@@ -1250,7 +1309,20 @@ static void createTransArmatureVerts(TransInfo *t)
                        else {
                                if (ebo->flag & BONE_TIPSEL) {
                                        copy_v3_v3(td->iloc, ebo->tail);
-                                       copy_v3_v3(td->center, (t->around == V3D_LOCAL) ? ebo->head : td->iloc);
+
+                                       /* Don't allow single selected tips to have a modified center,
+                                        * causes problem with snapping (see T45974).
+                                        * However, in rotation mode, we want to keep that 'rotate bone around root with
+                                        * only its tip selected' behavior (see T46325). */
+                                       if ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
+                                           ((t->mode == TFM_ROTATION) || (ebo->flag & BONE_ROOTSEL)))
+                                       {
+                                               copy_v3_v3(td->center, ebo->head);
+                                       }
+                                       else {
+                                               copy_v3_v3(td->center, td->iloc);
+                                       }
+
                                        td->loc = ebo->tail;
                                        td->flag = TD_SELECTED;
                                        if (ebo->flag & BONE_EDITMODE_LOCKED)
@@ -1298,7 +1370,7 @@ static void createTransArmatureVerts(TransInfo *t)
                }
 
                if (mirror && (td_old != td)) {
-                       eboflip = ED_armature_bone_get_mirrored(arm->edbo, ebo);
+                       eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo);
                        if (eboflip) {
                                bid[i].bone = eboflip;
                                bid[i].dist = eboflip->dist;
@@ -1329,18 +1401,18 @@ static void createTransMBallVerts(TransInfo *t)
        TransDataExtension *tx;
        float mtx[3][3], smtx[3][3];
        int count = 0, countsel = 0;
-       int propmode = t->flag & T_PROP_EDIT;
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
 
        /* count totals */
        for (ml = mb->editelems->first; ml; ml = ml->next) {
                if (ml->flag & SELECT) countsel++;
-               if (propmode) count++;
+               if (is_prop_edit) count++;
        }
 
        /* note: in prop mode we need at least 1 selected */
        if (countsel == 0) return;
 
-       if (propmode) t->total = count;
+       if (is_prop_edit) t->total = count;
        else t->total = countsel;
 
        td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(MBall EditMode)");
@@ -1350,7 +1422,7 @@ static void createTransMBallVerts(TransInfo *t)
        pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
 
        for (ml = mb->editelems->first; ml; ml = ml->next) {
-               if (propmode || (ml->flag & SELECT)) {
+               if (is_prop_edit || (ml->flag & SELECT)) {
                        td->loc = &ml->x;
                        copy_v3_v3(td->iloc, td->loc);
                        copy_v3_v3(td->center, td->loc);
@@ -1453,6 +1525,48 @@ static TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struc
        return hdata;
 }
 
+/**
+ * For the purpose of transform code we need to behave as if handles are selected,
+ * even when they aren't (see special case below).
+ */
+static int bezt_select_to_transform_triple_flag(
+        const BezTriple *bezt, const bool hide_handles)
+{
+       int flag = 0;
+
+       if (hide_handles) {
+               if (bezt->f2 & SELECT) {
+                       flag = (1 << 0) | (1 << 1) | (1 << 2);
+               }
+       }
+       else {
+               flag = (
+                       ((bezt->f1 & SELECT) ? (1 << 0) : 0) |
+                       ((bezt->f2 & SELECT) ? (1 << 1) : 0) |
+                       ((bezt->f3 & SELECT) ? (1 << 2) : 0)
+               );
+       }
+
+       /* Special case for auto & aligned handles:
+        * When a center point is being moved without the handles,
+        * leaving the handles stationary makes no sense and only causes strange behavior,
+        * where one handle is arbitrarily anchored, the other one is aligned and lengthened
+        * based on where the center point is moved. Also a bug when cancelling, see: T52007.
+        *
+        * A more 'correct' solution could be to store handle locations in 'TransDataCurveHandleFlags'.
+        * However that doesn't resolve odd behavior, so best transform the handles in this case.
+        */
+       if ((flag != ((1 << 0) | (1 << 1) | (1 << 2))) && (flag & (1 << 1))) {
+               if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) &&
+                   ELEM(bezt->h2, HD_AUTO, HD_ALIGN))
+               {
+                       flag = (1 << 0) | (1 << 1) | (1 << 2);
+               }
+       }
+
+       return flag;
+}
+
 static void createTransCurveVerts(TransInfo *t)
 {
        Curve *cu = t->obedit->data;
@@ -1463,36 +1577,36 @@ static void createTransCurveVerts(TransInfo *t)
        float mtx[3][3], smtx[3][3];
        int a;
        int count = 0, countsel = 0;
-       int propmode = t->flag & T_PROP_EDIT;
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
        short hide_handles = (cu->drawflag & CU_HIDE_HANDLES);
        ListBase *nurbs;
 
        /* to be sure */
        if (cu->editnurb == NULL) return;
 
+#define SEL_F1 (1 << 0)
+#define SEL_F2 (1 << 1)
+#define SEL_F3 (1 << 2)
+
        /* count total of vertices, check identical as in 2nd loop for making transdata! */
        nurbs = BKE_curve_editNurbs_get(cu);
        for (nu = nurbs->first; nu; nu = nu->next) {
                if (nu->type == CU_BEZIER) {
                        for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
                                if (bezt->hide == 0) {
-                                       if (hide_handles) {
-                                               if (bezt->f2 & SELECT) countsel += 3;
-                                               if (propmode) count += 3;
-                                       }
-                                       else {
-                                               if (bezt->f1 & SELECT) countsel++;
-                                               if (bezt->f2 & SELECT) countsel++;
-                                               if (bezt->f3 & SELECT) countsel++;
-                                               if (propmode) count += 3;
-                                       }
+                                       const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles);
+                                       if (bezt_tx & SEL_F1) { countsel++; }
+                                       if (bezt_tx & SEL_F2) { countsel++; }
+                                       if (bezt_tx & SEL_F3) { countsel++; }
+                                       if (is_prop_edit) count += 3;
+
                                }
                        }
                }
                else {
                        for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
                                if (bp->hide == 0) {
-                                       if (propmode) count++;
+                                       if (is_prop_edit) count++;
                                        if (bp->f1 & SELECT) countsel++;
                                }
                        }
@@ -1501,7 +1615,7 @@ static void createTransCurveVerts(TransInfo *t)
        /* note: in prop mode we need at least 1 selected */
        if (countsel == 0) return;
 
-       if (propmode) t->total = count;
+       if (is_prop_edit) t->total = count;
        else t->total = countsel;
        t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Curve EditMode)");
 
@@ -1520,7 +1634,7 @@ static void createTransCurveVerts(TransInfo *t)
                                        TransDataCurveHandleFlags *hdata = NULL;
                                        float axismtx[3][3];
 
-                                       if (t->around == V3D_LOCAL) {
+                                       if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
                                                float normal[3], plane[3];
 
                                                BKE_nurb_bezt_calc_normal(nu, bezt, normal);
@@ -1536,14 +1650,14 @@ static void createTransCurveVerts(TransInfo *t)
                                                }
                                        }
 
-                                       if (propmode ||
-                                           ((bezt->f2 & SELECT) && hide_handles) ||
-                                           ((bezt->f1 & SELECT) && hide_handles == 0))
-                                       {
+                                       /* Elements that will be transform (not always a match to selection). */
+                                       const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles);
+
+                                       if (is_prop_edit || bezt_tx & SEL_F1) {
                                                copy_v3_v3(td->iloc, bezt->vec[0]);
                                                td->loc = bezt->vec[0];
                                                copy_v3_v3(td->center, bezt->vec[(hide_handles ||
-                                                                                 (t->around == V3D_LOCAL) ||
+                                                                                 (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
                                                                                  (bezt->f2 & SELECT)) ? 1 : 0]);
                                                if (hide_handles) {
                                                        if (bezt->f2 & SELECT) td->flag = TD_SELECTED;
@@ -1560,7 +1674,7 @@ static void createTransCurveVerts(TransInfo *t)
 
                                                copy_m3_m3(td->smtx, smtx);
                                                copy_m3_m3(td->mtx, mtx);
-                                               if (t->around == V3D_LOCAL) {
+                                               if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
                                                        copy_m3_m3(td->axismtx, axismtx);
                                                }
 
@@ -1570,7 +1684,7 @@ static void createTransCurveVerts(TransInfo *t)
                                        }
 
                                        /* This is the Curve Point, the other two are handles */
-                                       if (propmode || (bezt->f2 & SELECT)) {
+                                       if (is_prop_edit || bezt_tx & SEL_F2) {
                                                copy_v3_v3(td->iloc, bezt->vec[1]);
                                                td->loc = bezt->vec[1];
                                                copy_v3_v3(td->center, td->loc);
@@ -1592,11 +1706,11 @@ static void createTransCurveVerts(TransInfo *t)
 
                                                copy_m3_m3(td->smtx, smtx);
                                                copy_m3_m3(td->mtx, mtx);
-                                               if (t->around == V3D_LOCAL) {
+                                               if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
                                                        copy_m3_m3(td->axismtx, axismtx);
                                                }
 
-                                               if ((bezt->f1 & SELECT) == 0 && (bezt->f3 & SELECT) == 0)
+                                               if ((bezt_tx & SEL_F1) == 0 && (bezt_tx & SEL_F3) == 0)
                                                        /* If the middle is selected but the sides arnt, this is needed */
                                                        if (hdata == NULL) { /* if the handle was not saved by the previous handle */
                                                                hdata = initTransDataCurveHandles(td, bezt);
@@ -1606,14 +1720,11 @@ static void createTransCurveVerts(TransInfo *t)
                                                count++;
                                                tail++;
                                        }
-                                       if (propmode ||
-                                           ((bezt->f2 & SELECT) && hide_handles) ||
-                                           ((bezt->f3 & SELECT) && hide_handles == 0))
-                                       {
+                                       if (is_prop_edit || bezt_tx & SEL_F3) {
                                                copy_v3_v3(td->iloc, bezt->vec[2]);
                                                td->loc = bezt->vec[2];
                                                copy_v3_v3(td->center, bezt->vec[(hide_handles ||
-                                                                                 (t->around == V3D_LOCAL) ||
+                                                                                 (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
                                                                                  (bezt->f2 & SELECT)) ? 1 : 2]);
                                                if (hide_handles) {
                                                        if (bezt->f2 & SELECT) td->flag = TD_SELECTED;
@@ -1632,7 +1743,7 @@ static void createTransCurveVerts(TransInfo *t)
 
                                                copy_m3_m3(td->smtx, smtx);
                                                copy_m3_m3(td->mtx, mtx);
-                                               if (t->around == V3D_LOCAL) {
+                                               if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
                                                        copy_m3_m3(td->axismtx, axismtx);
                                                }
 
@@ -1643,17 +1754,17 @@ static void createTransCurveVerts(TransInfo *t)
 
                                        (void)hdata;  /* quiet warning */
                                }
-                               else if (propmode && head != tail) {
+                               else if (is_prop_edit && head != tail) {
                                        calc_distanceCurveVerts(head, tail - 1);
                                        head = tail;
                                }
                        }
-                       if (propmode && head != tail)
+                       if (is_prop_edit && head != tail)
                                calc_distanceCurveVerts(head, tail - 1);
 
                        /* TODO - in the case of tilt and radius we can also avoid allocating the initTransDataCurveHandles
                         * but for now just don't change handle types */
-                       if (ELEM(t->mode, TFM_CURVE_SHRINKFATTEN, TFM_TILT) == 0) {
+                       if (ELEM(t->mode, TFM_CURVE_SHRINKFATTEN, TFM_TILT, TFM_DUMMY) == 0) {
                                /* sets the handles based on their selection, do this after the data is copied to the TransData */
                                BKE_nurb_handles_test(nu, !hide_handles);
                        }
@@ -1663,7 +1774,27 @@ static void createTransCurveVerts(TransInfo *t)
                        head = tail = td;
                        for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
                                if (bp->hide == 0) {
-                                       if (propmode || (bp->f1 & SELECT)) {
+                                       if (is_prop_edit || (bp->f1 & SELECT)) {
+                                               float axismtx[3][3];
+
+                                               if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
+                                                       if (nu->pntsv == 1) {
+                                                               float normal[3], plane[3];
+
+                                                               BKE_nurb_bpoint_calc_normal(nu, bp, normal);
+                                                               BKE_nurb_bpoint_calc_plane(nu, bp, plane);
+
+                                                               if (createSpaceNormalTangent(axismtx, normal, plane)) {
+                                                                       /* pass */
+                                                               }
+                                                               else {
+                                                                       normalize_v3(normal);
+                                                                       axis_dominant_v3_to_m3(axismtx, normal);
+                                                                       invert_m3(axismtx);
+                                                               }
+                                                       }
+                                               }
+
                                                copy_v3_v3(td->iloc, bp->vec);
                                                td->loc = bp->vec;
                                                copy_v3_v3(td->center, td->loc);
@@ -1682,21 +1813,30 @@ static void createTransCurveVerts(TransInfo *t)
 
                                                copy_m3_m3(td->smtx, smtx);
                                                copy_m3_m3(td->mtx, mtx);
+                                               if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
+                                                       if (nu->pntsv == 1) {
+                                                               copy_m3_m3(td->axismtx, axismtx);
+                                                       }
+                                               }
 
                                                td++;
                                                count++;
                                                tail++;
                                        }
                                }
-                               else if (propmode && head != tail) {
+                               else if (is_prop_edit && head != tail) {
                                        calc_distanceCurveVerts(head, tail - 1);
                                        head = tail;
                                }
                        }
-                       if (propmode && head != tail)
+                       if (is_prop_edit && head != tail)
                                calc_distanceCurveVerts(head, tail - 1);
                }
        }
+
+#undef SEL_F1
+#undef SEL_F2
+#undef SEL_F3
 }
 
 /* ********************* lattice *************** */
@@ -1709,14 +1849,14 @@ static void createTransLatticeVerts(TransInfo *t)
        float mtx[3][3], smtx[3][3];
        int a;
        int count = 0, countsel = 0;
-       int propmode = t->flag & T_PROP_EDIT;
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
 
        bp = latt->def;
        a  = latt->pntsu * latt->pntsv * latt->pntsw;
        while (a--) {
                if (bp->hide == 0) {
                        if (bp->f1 & SELECT) countsel++;
-                       if (propmode) count++;
+                       if (is_prop_edit) count++;
                }
                bp++;
        }
@@ -1724,7 +1864,7 @@ static void createTransLatticeVerts(TransInfo *t)
        /* note: in prop mode we need at least 1 selected */
        if (countsel == 0) return;
 
-       if (propmode) t->total = count;
+       if (is_prop_edit) t->total = count;
        else t->total = countsel;
        t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Lattice EditMode)");
 
@@ -1735,7 +1875,7 @@ static void createTransLatticeVerts(TransInfo *t)
        bp = latt->def;
        a  = latt->pntsu * latt->pntsv * latt->pntsw;
        while (a--) {
-               if (propmode || (bp->f1 & SELECT)) {
+               if (is_prop_edit || (bp->f1 & SELECT)) {
                        if (bp->hide == 0) {
                                copy_v3_v3(td->iloc, bp->vec);
                                td->loc = bp->vec;
@@ -1765,7 +1905,6 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
 {
        TransData *td = NULL;
        TransDataExtension *tx;
-       Base *base = CTX_data_active_base(C);
        Object *ob = CTX_data_active_object(C);
        ParticleEditSettings *pset = PE_settings(t->scene);
        PTCacheEdit *edit = PE_get_current(t->scene, ob);
@@ -1776,7 +1915,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
        float mat[4][4];
        int i, k, transformparticle;
        int count = 0, hasselected = 0;
-       int propmode = t->flag & T_PROP_EDIT;
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
 
        if (edit == NULL || t->settings->particle.selectmode == SCE_SELECT_PATH) return;
 
@@ -1785,8 +1924,6 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
        if (psys)
                psmd = psys_get_modifier(ob, psys);
 
-       base->flag |= BA_HAS_RECALC_DATA;
-
        for (i = 0, point = edit->points; i < edit->totpoint; i++, point++) {
                point->flag &= ~PEP_TRANSFORM;
                transformparticle = 0;
@@ -1798,7 +1935,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
                                                hasselected = 1;
                                                transformparticle = 1;
                                        }
-                                       else if (propmode)
+                                       else if (is_prop_edit)
                                                transformparticle = 1;
                                }
                        }
@@ -1832,7 +1969,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
                if (!(point->flag & PEP_TRANSFORM)) continue;
 
                if (psys && !(psys->flag & PSYS_GLOBAL_HAIR))
-                       psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles + i, mat);
+                       psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles + i, mat);
 
                for (k = 0, key = point->keys; k < point->totkey; k++, key++) {
                        if (key->flag & PEK_USE_WCO) {
@@ -1848,7 +1985,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
 
                        if (key->flag & PEK_SELECT)
                                td->flag |= TD_SELECTED;
-                       else if (!propmode)
+                       else if (!is_prop_edit)
                                td->flag |= TD_SKIP;
 
                        unit_m3(td->mtx);
@@ -1877,7 +2014,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
                                tx++;
                        tail++;
                }
-               if (propmode && head != tail)
+               if (is_prop_edit && head != tail)
                        calc_distanceCurveVerts(head, tail - 1);
        }
 }
@@ -1893,7 +2030,8 @@ void flushTransParticles(TransInfo *t)
        PTCacheEditKey *key;
        TransData *td;
        float mat[4][4], imat[4][4], co[3];
-       int i, k, propmode = t->flag & T_PROP_EDIT;
+       int i, k;
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
 
        if (psys)
                psmd = psys_get_modifier(ob, psys);
@@ -1905,7 +2043,7 @@ void flushTransParticles(TransInfo *t)
                if (!(point->flag & PEP_TRANSFORM)) continue;
 
                if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
-                       psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles + i, mat);
+                       psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles + i, mat);
                        invert_m4_m4(imat, mat);
 
                        for (k = 0, key = point->keys; k < point->totkey; k++, key++) {
@@ -1914,7 +2052,7 @@ void flushTransParticles(TransInfo *t)
 
 
                                /* optimization for proportional edit */
-                               if (!propmode || !compare_v3v3(key->co, co, 0.0001f)) {
+                               if (!is_prop_edit || !compare_v3v3(key->co, co, 0.0001f)) {
                                        copy_v3_v3(key->co, co);
                                        point->flag |= PEP_EDIT_RECALC;
                                }
@@ -1929,9 +2067,12 @@ void flushTransParticles(TransInfo *t)
 
 /* ********************* mesh ****************** */
 
-static bool bmesh_test_dist_add(BMVert *v, BMVert *v_other,
-                                float *dists, const float *dists_prev,
-                                float mtx[3][3])
+static bool bmesh_test_dist_add(
+        BMVert *v, BMVert *v_other,
+        float *dists, const float *dists_prev,
+        /* optionally track original index */
+        int *index, const int *index_prev,
+        float mtx[3][3])
 {
        if ((BM_elem_flag_test(v_other, BM_ELEM_SELECT) == 0) &&
            (BM_elem_flag_test(v_other, BM_ELEM_HIDDEN) == 0))
@@ -1946,6 +2087,9 @@ static bool bmesh_test_dist_add(BMVert *v, BMVert *v_other,
                dist_other = dists_prev[i] + len_v3(vec);
                if (dist_other < dists[i_other]) {
                        dists[i_other] = dist_other;
+                       if (index != NULL) {
+                               index[i_other] = index_prev[i];
+                       }
                        return true;
                }
        }
@@ -1953,11 +2097,13 @@ static bool bmesh_test_dist_add(BMVert *v, BMVert *v_other,
        return false;
 }
 
-static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float *dists)
+/**
+ * \param mtx: Measure disatnce in this space.
+ * \param dists: Store the closest connected distance to selected vertices.
+ * \param index: Optionally store the original index we're measuring the distance to (can be NULL).
+ */
+static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float *dists, int *index)
 {
-       /* need to be very careful of feedback loops here, store previous dist's to avoid feedback */
-       float *dists_prev = MEM_mallocN(bm->totvert * sizeof(float), __func__);
-
        BLI_LINKSTACK_DECLARE(queue, BMVert *);
 
        /* any BM_ELEM_TAG'd vertex is in 'queue_next', so we don't add in twice */
@@ -1972,74 +2118,113 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float
                int i;
 
                BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
+                       float dist;
                        BM_elem_index_set(v, i); /* set_inline */
                        BM_elem_flag_disable(v, BM_ELEM_TAG);
 
                        if (BM_elem_flag_test(v, BM_ELEM_SELECT) == 0 || BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
-                               dists[i] = FLT_MAX;
+                               dist = FLT_MAX;
+                               if (index != NULL) {
+                                       index[i] = i;
+                               }
                        }
                        else {
                                BLI_LINKSTACK_PUSH(queue, v);
-
-                               dists[i] = 0.0f;
+                               dist = 0.0f;
+                               if (index != NULL) {
+                                       index[i] = i;
+                               }
                        }
+
+                       dists[i] = dist;
                }
                bm->elem_index_dirty &= ~BM_VERT;
        }
 
+       /* need to be very careful of feedback loops here, store previous dist's to avoid feedback */
+       float *dists_prev = MEM_dupallocN(dists);
+       int *index_prev = MEM_dupallocN(index);  /* may be NULL */
+
        do {
                BMVert *v;
                LinkNode *lnk;
 
+               /* this is correct but slow to do each iteration,
+                * instead sync the dist's while clearing BM_ELEM_TAG (below) */
+#if 0
                memcpy(dists_prev, dists, sizeof(float) * bm->totvert);
+#endif
 
                while ((v = BLI_LINKSTACK_POP(queue))) {
-                       /* quick checks */
-                       bool has_edges = (v->e != NULL);
-                       bool has_faces = false;
+                       BLI_assert(dists[BM_elem_index_get(v)] != FLT_MAX);
 
                        /* connected edge-verts */
-                       if (has_edges) {
-                               BMIter iter;
-                               BMEdge *e;
+                       if (v->e != NULL) {
+                               BMEdge *e_iter, *e_first;
 
-                               BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
-                                       has_faces |= (BM_edge_is_wire(e) == false);
+                               e_iter = e_first = v->e;
 
-                                       if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == 0) {
-                                               BMVert *v_other = BM_edge_other_vert(e, v);
-                                               if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) {
-                                                       if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) {
-                                                               BM_elem_flag_enable(v_other, BM_ELEM_TAG);
-                                                               BLI_LINKSTACK_PUSH(queue_next, v_other);
+                               /* would normally use BM_EDGES_OF_VERT, but this runs so often,
+                                * its faster to iterate on the data directly */
+                               do {
+
+                                       if (BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == 0) {
+
+                                               /* edge distance */
+                                               {
+                                                       BMVert *v_other = BM_edge_other_vert(e_iter, v);
+                                                       if (bmesh_test_dist_add(v, v_other, dists, dists_prev, index, index_prev, mtx)) {
+                                                               if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) {
+                                                                       BM_elem_flag_enable(v_other, BM_ELEM_TAG);
+                                                                       BLI_LINKSTACK_PUSH(queue_next, v_other);
+                                                               }
                                                        }
                                                }
-                                       }
-                               }
-                       }
-                       
-                       /* imaginary edge diagonally across quad */
-                       if (has_faces) {
-                               BMIter iter;
-                               BMLoop *l;
-
-                               BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
-                                       if ((BM_elem_flag_test(l->f, BM_ELEM_HIDDEN) == 0) && (l->f->len == 4)) {
-                                               BMVert *v_other = l->next->next->v;
-                                               if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) {
-                                                       if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) {
-                                                               BM_elem_flag_enable(v_other, BM_ELEM_TAG);
-                                                               BLI_LINKSTACK_PUSH(queue_next, v_other);
-                                                       }
+
+                                               /* face distance */
+                                               if (e_iter->l) {
+                                                       BMLoop *l_iter_radial, *l_first_radial;
+                                                       /**
+                                                        * imaginary edge diagonally across quad,
+                                                        * \note, this takes advantage of the rules of winding that we
+                                                        * know 2 or more of a verts edges wont reference the same face twice.
+                                                        * Also, if the edge is hidden, the face will be hidden too.
+                                                        */
+                                                       l_iter_radial = l_first_radial = e_iter->l;
+
+                                                       do {
+                                                               if ((l_iter_radial->v == v) &&
+                                                                   (l_iter_radial->f->len == 4) &&
+                                                                   (BM_elem_flag_test(l_iter_radial->f, BM_ELEM_HIDDEN) == 0))
+                                                               {
+                                                                       BMVert *v_other = l_iter_radial->next->next->v;
+                                                                       if (bmesh_test_dist_add(v, v_other, dists, dists_prev, index, index_prev, mtx)) {
+                                                                               if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) {
+                                                                                       BM_elem_flag_enable(v_other, BM_ELEM_TAG);
+                                                                                       BLI_LINKSTACK_PUSH(queue_next, v_other);
+                                                                               }
+                                                                       }
+                                                               }
+                                                       } while ((l_iter_radial = l_iter_radial->radial_next) != l_first_radial);
                                                }
                                        }
-                               }
+                               } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
                        }
                }
 
+
                /* clear for the next loop */
                for (lnk = queue_next; lnk; lnk = lnk->next) {
-                       BM_elem_flag_disable((BMVert *)lnk->link, BM_ELEM_TAG);
+                       BMVert *v_link = lnk->link;
+                       const int i = BM_elem_index_get(v_link);
+
+                       BM_elem_flag_disable(v_link, BM_ELEM_TAG);
+
+                       /* keep in sync, avoid having to do full memcpy each iteration */
+                       dists_prev[i] = dists[i];
+                       if (index != NULL) {
+                               index_prev[i] = index[i];
+                       }
                }
 
                BLI_LINKSTACK_SWAP(queue, queue_next);
@@ -2053,9 +2238,14 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float
        BLI_LINKSTACK_FREE(queue_next);
 
        MEM_freeN(dists_prev);
+       if (index_prev != NULL) {
+               MEM_freeN(index_prev);
+       }
 }
 
-static struct TransIslandData *editmesh_islands_info_calc(BMEditMesh *em, int *r_island_tot, int **r_island_vert_map)
+static struct TransIslandData *editmesh_islands_info_calc(
+        BMEditMesh *em, int *r_island_tot, int **r_island_vert_map,
+        bool calc_single_islands)
 {
        BMesh *bm = em->bm;
        struct TransIslandData *trans_islands;
@@ -2097,7 +2287,7 @@ static struct TransIslandData *editmesh_islands_info_calc(BMEditMesh *em, int *r
        vert_map = MEM_mallocN(sizeof(*vert_map) * bm->totvert, __func__);
        /* we shouldn't need this, but with incorrect selection flushing
         * its possible we have a selected vertex thats not in a face, for now best not crash in that case. */
-       fill_vn_i(vert_map, bm->totvert, -1);
+       copy_vn_i(vert_map, bm->totvert, -1);
 
        BM_mesh_elem_table_ensure(bm, htype);
        ele_array = (htype == BM_FACE) ? (void **)bm->ftable : (void **)bm->etable;
@@ -2167,6 +2357,42 @@ static struct TransIslandData *editmesh_islands_info_calc(BMEditMesh *em, int *r
        MEM_freeN(groups_array);
        MEM_freeN(group_index);
 
+       /* for PET we need islands of 1 so connected vertices can use it with V3D_AROUND_LOCAL_ORIGINS */
+       if (calc_single_islands) {
+               BMIter viter;
+               BMVert *v;
+               int group_tot_single = 0;
+
+               BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
+                       if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (vert_map[i] == -1)) {
+                               group_tot_single += 1;
+                       }
+               }
+
+               if (group_tot_single != 0) {
+                       trans_islands = MEM_reallocN(trans_islands, sizeof(*trans_islands) * (group_tot + group_tot_single));
+
+                       BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
+                               if (BM_elem_flag_test(v, BM_ELEM_SELECT) && (vert_map[i] == -1)) {
+                                       struct TransIslandData *v_island = &trans_islands[group_tot];
+                                       vert_map[i] = group_tot;
+
+                                       copy_v3_v3(v_island->co, v->co);
+
+                                       if (is_zero_v3(v->no) != 0.0f) {
+                                               axis_dominant_v3_to_m3(v_island->axismtx, v->no);
+                                               invert_m3(v_island->axismtx);
+                                       }
+                                       else {
+                                               unit_m3(v_island->axismtx);
+                                       }
+
+                                       group_tot += 1;
+                               }
+                       }
+               }
+       }
+
        *r_island_tot = group_tot;
        *r_island_vert_map = vert_map;
 
@@ -2191,7 +2417,7 @@ static void VertsToTransData(TransInfo *t, TransData *td, TransDataExtension *tx
        if ((t->mode == TFM_SHRINKFATTEN) &&
            (em->selectmode & SCE_SELECT_FACE) &&
            BM_elem_flag_test(eve, BM_ELEM_SELECT) &&
-           (BM_vert_normal_update_ex(eve, BM_ELEM_SELECT, _no)))
+           (BM_vert_calc_normal_ex(eve, BM_ELEM_SELECT, _no)))
        {
                no = _no;
        }
@@ -2203,7 +2429,7 @@ static void VertsToTransData(TransInfo *t, TransData *td, TransDataExtension *tx
                copy_v3_v3(td->center, v_island->co);
                copy_m3_m3(td->axismtx, v_island->axismtx);
        }
-       else if (t->around == V3D_LOCAL) {
+       else if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
                copy_v3_v3(td->center, td->loc);
                createSpaceNormal(td->axismtx, no);
        }
@@ -2257,7 +2483,7 @@ static void createTransEditVerts(TransInfo *t)
        float mtx[3][3], smtx[3][3], (*defmats)[3][3] = NULL, (*defcos)[3] = NULL;
        float *dists = NULL;
        int a;
-       int propmode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
+       const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
        int mirror = 0;
        int cd_vert_bweight_offset = -1;
        bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
@@ -2266,30 +2492,25 @@ static void createTransEditVerts(TransInfo *t)
        int island_info_tot;
        int *island_vert_map = NULL;
 
+       /* Even for translation this is needed because of island-orientation, see: T51651. */
+       const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS);
+       /* Original index of our connected vertex when connected distances are calculated.
+        * Optional, allocate if needed. */
+       int *dists_index = NULL;
+
        if (t->flag & T_MIRROR) {
                EDBM_verts_mirror_cache_begin(em, 0, false, (t->flag & T_PROP_EDIT) == 0, use_topology);
                mirror = 1;
        }
 
-       /* quick check if we can transform */
-       /* note: in prop mode we need at least 1 selected */
-       if (em->selectmode & SCE_SELECT_VERTEX) {
-               if (bm->totvertsel == 0) {
-                       goto cleanup;
-               }
-       }
-       else if (em->selectmode & SCE_SELECT_EDGE) {
-               if (bm->totvertsel == 0 || bm->totedgesel == 0) {
-                       goto cleanup;
-               }
-       }
-       else if (em->selectmode & SCE_SELECT_FACE) {
-               if (bm->totvertsel == 0 || bm->totfacesel == 0) {
-                       goto cleanup;
-               }
-       }
-       else {
-               BLI_assert(0);
+       /**
+        * Quick check if we can transform.
+        *
+        * \note ignore modes here, even in edge/face modes, transform data is created by selected vertices.
+        * \note in prop mode we need at least 1 selected.
+        */
+       if (bm->totvertsel == 0) {
+               goto cleanup;
        }
 
        if (t->mode == TFM_BWEIGHT) {
@@ -2297,7 +2518,7 @@ static void createTransEditVerts(TransInfo *t)
                cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
        }
 
-       if (propmode) {
+       if (prop_mode) {
                unsigned int count = 0;
                BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
                        if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
@@ -2308,8 +2529,12 @@ static void createTransEditVerts(TransInfo *t)
                t->total = count;
 
                /* allocating scratch arrays */
-               if (propmode & T_PROP_CONNECTED)
-                       dists = MEM_mallocN(em->bm->totvert * sizeof(float), "scratch nears");
+               if (prop_mode & T_PROP_CONNECTED) {
+                       dists = MEM_mallocN(em->bm->totvert * sizeof(float), __func__);
+                       if (is_island_center) {
+                               dists_index =  MEM_mallocN(em->bm->totvert * sizeof(int), __func__);
+                       }
+               }
        }
        else {
                t->total = bm->totvertsel;
@@ -2330,12 +2555,18 @@ static void createTransEditVerts(TransInfo *t)
         * matrix inversion still works and we can still moving along the other */
        pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
 
-       if (propmode & T_PROP_CONNECTED) {
-               editmesh_set_connectivity_distance(em->bm, mtx, dists);
+       if (prop_mode & T_PROP_CONNECTED) {
+               editmesh_set_connectivity_distance(em->bm, mtx, dists, dists_index);
        }
 
-       if (t->around == V3D_LOCAL) {
-               island_info = editmesh_islands_info_calc(em, &island_info_tot, &island_vert_map);
+       if (is_island_center) {
+               /* In this specific case, near-by vertices will need to know the island of the nearest connected vertex. */
+               const bool calc_single_islands = (
+                       (prop_mode & T_PROP_CONNECTED) &&
+                       (t->around == V3D_AROUND_LOCAL_ORIGINS) &&
+                       (em->selectmode & SCE_SELECT_VERTEX));
+
+               island_info = editmesh_islands_info_calc(em, &island_info_tot, &island_vert_map, calc_single_islands);
        }
 
        /* detect CrazySpace [tm] */
@@ -2344,7 +2575,7 @@ static void createTransEditVerts(TransInfo *t)
                if (modifiers_isCorrectableDeformed(t->scene, t->obedit)) {
                        /* check if we can use deform matrices for modifier from the
                         * start up to stack, they are more accurate than quats */
-                       totleft = editbmesh_get_first_deform_matrices(t->scene, t->obedit, em, &defmats, &defcos);
+                       totleft = BKE_crazyspace_get_first_deform_matrices_editbmesh(t->scene, t->obedit, em, &defmats, &defcos);
                }
 
                /* if we still have more modifiers, also do crazyspace
@@ -2359,7 +2590,7 @@ static void createTransEditVerts(TransInfo *t)
                {
                        mappedcos = BKE_crazyspace_get_mapped_editverts(t->scene, t->obedit);
                        quats = MEM_mallocN(em->bm->totvert * sizeof(*quats), "crazy quats");
-                       BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !propmode);
+                       BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !prop_mode);
                        if (mappedcos)
                                MEM_freeN(mappedcos);
                }
@@ -2384,11 +2615,17 @@ static void createTransEditVerts(TransInfo *t)
 
        BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
                if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
-                       if (propmode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
-                               struct TransIslandData *v_island = (island_info && island_vert_map[a] != -1) ?
-                                                                  &island_info[island_vert_map[a]] : NULL;
+                       if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+                               struct TransIslandData *v_island = NULL;
                                float *bweight = (cd_vert_bweight_offset != -1) ? BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : NULL;
 
+                               if (island_info) {
+                                       const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a;
+                                       v_island = (island_vert_map[connected_index] != -1) ?
+                                                  &island_info[island_vert_map[connected_index]] : NULL;
+                               }
+
+
                                VertsToTransData(t, tob, tx, em, eve, bweight, v_island);
                                if (tx)
                                        tx++;
@@ -2397,8 +2634,8 @@ static void createTransEditVerts(TransInfo *t)
                                if (BM_elem_flag_test(eve, BM_ELEM_SELECT))
                                        tob->flag |= TD_SELECTED;
 
-                               if (propmode) {
-                                       if (propmode & T_PROP_CONNECTED) {
+                               if (prop_mode) {
+                                       if (prop_mode & T_PROP_CONNECTED) {
                                                tob->dist = dists[a];
                                        }
                                        else {
@@ -2444,7 +2681,7 @@ static void createTransEditVerts(TransInfo *t)
                        }
                }
        }
-       
+
        if (island_info) {
                MEM_freeN(island_info);
                MEM_freeN(island_vert_map);
@@ -2467,6 +2704,8 @@ cleanup:
                MEM_freeN(defmats);
        if (dists)
                MEM_freeN(dists);
+       if (dists_index)
+               MEM_freeN(dists_index);
 
        if (t->flag & T_MIRROR) {
                EDBM_verts_mirror_cache_end(em);
@@ -2480,9 +2719,9 @@ void flushTransNodes(TransInfo *t)
        int a;
        TransData *td;
        TransData2D *td2d;
-       
+
        applyGridAbsolute(t);
-       
+
        /* flush to 2d vector from internally used 3d vector */
        for (a = 0, td = t->data, td2d = t->data2d; a < t->total; a++, td++, td2d++) {
                bNode *node = td->extra;
@@ -2496,7 +2735,7 @@ void flushTransNodes(TransInfo *t)
                locx = td2d->loc[0] / dpi_fac;
                locy = td2d->loc[1] / dpi_fac;
 #endif
-               
+
                /* account for parents (nested nodes) */
                if (node->parent) {
                        nodeFromView(node->parent, locx, locy, &node->locx, &node->locy);
@@ -2506,7 +2745,7 @@ void flushTransNodes(TransInfo *t)
                        node->locy = locy;
                }
        }
-       
+
        /* handle intersection with noodles */
        if (t->total == 1) {
                ED_node_link_intersect_test(t->sa, 1);
@@ -2559,7 +2798,7 @@ void flushTransSeq(TransInfo *t)
                tdsq = (TransDataSeq *)td->extra;
                seq = tdsq->seq;
                old_start = seq->start;
-               new_frame = iroundf(td2d->loc[0]);
+               new_frame = round_fl_to_int(td2d->loc[0]);
 
                switch (tdsq->sel_flag) {
                        case SELECT:
@@ -2571,7 +2810,7 @@ void flushTransSeq(TransInfo *t)
                                        seq->start = new_frame - tdsq->start_offset;
 #endif
                                if (seq->depth == 0) {
-                                       seq->machine = iroundf(td2d->loc[1]);
+                                       seq->machine = round_fl_to_int(td2d->loc[1]);
                                        CLAMP(seq->machine, 1, MAXSEQ);
                                }
                                break;
@@ -2625,6 +2864,20 @@ void flushTransSeq(TransInfo *t)
                                BKE_sequence_calc(t->scene, seq);
                        }
                }
+
+               /* update effects inside meta's */
+               for (a = 0, seq_prev = NULL, td = t->data, td2d = t->data2d;
+                    a < t->total;
+                    a++, td++, td2d++, seq_prev = seq)
+               {
+                       tdsq = (TransDataSeq *)td->extra;
+                       seq = tdsq->seq;
+                       if ((seq != seq_prev) && (seq->depth != 0)) {
+                               if (seq->seq1 || seq->seq2 || seq->seq3) {
+                                       BKE_sequence_calc(t->scene, seq);
+                               }
+                       }
+               }
        }
 
        /* need to do the overlap check in a new loop otherwise adjacent strips
@@ -2650,24 +2903,23 @@ void flushTransSeq(TransInfo *t)
 
 /* ********************* UV ****************** */
 
-static void UVsToTransData(SpaceImage *sima, TransData *td, TransData2D *td2d, float *uv, int selected)
+static void UVsToTransData(
+        const float aspect[2], TransData *td, TransData2D *td2d,
+        float *uv, const float *center, bool selected)
 {
-       float aspx, aspy;
-
-       ED_space_image_get_uv_aspect(sima, &aspx, &aspy);
-
        /* uv coords are scaled by aspects. this is needed for rotations and
         * proportional editing to be consistent with the stretched uv coords
         * that are displayed. this also means that for display and numinput,
         * and when the uv coords are flushed, these are converted each time */
-       td2d->loc[0] = uv[0] * aspx;
-       td2d->loc[1] = uv[1] * aspy;
+       td2d->loc[0] = uv[0] * aspect[0];
+       td2d->loc[1] = uv[1] * aspect[1];
        td2d->loc[2] = 0.0f;
        td2d->loc2d = uv;
 
        td->flag = 0;
        td->loc = td2d->loc;
-       copy_v3_v3(td->center, td->loc);
+       copy_v2_v2(td->center, center ? center : td->loc);
+       td->center[2] = 0.0f;
        copy_v3_v3(td->iloc, td->loc);
 
        memset(td->axismtx, 0, sizeof(td->axismtx));
@@ -2694,36 +2946,43 @@ static void createTransUVs(bContext *C, TransInfo *t)
        ToolSettings *ts = CTX_data_tool_settings(C);
        TransData *td = NULL;
        TransData2D *td2d = NULL;
-       MTexPoly *tf;
-       MLoopUV *luv;
        BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
        BMFace *efa;
-       BMLoop *l;
        BMIter iter, liter;
        UvElementMap *elementmap = NULL;
        BLI_bitmap *island_enabled = NULL;
+       struct { float co[2]; int co_num; } *island_center = NULL;
        int count = 0, countsel = 0, count_rejected = 0;
-       const bool propmode = (t->flag & T_PROP_EDIT) != 0;
-       const bool propconnected = (t->flag & T_PROP_CONNECTED) != 0;
-
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
+       const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
+       const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS);
        const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+       const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY);
 
-       if (!ED_space_image_show_uvedit(sima, t->obedit)) return;
+       if (!ED_space_image_show_uvedit(sima, t->obedit))
+               return;
 
        /* count */
-       if (propconnected) {
+       if (is_prop_connected || is_island_center) {
                /* create element map with island information */
-               if (ts->uv_flag & UV_SYNC_SELECTION) {
-                       elementmap = BM_uv_element_map_create(em->bm, false, true);
+               const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0;
+               elementmap = BM_uv_element_map_create(em->bm, use_facesel, false, true);
+               if (elementmap == NULL) {
+                       return;
                }
-               else {
-                       elementmap = BM_uv_element_map_create(em->bm, true, true);
+
+               if (is_prop_connected) {
+                       island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)");
+               }
+
+               if (is_island_center) {
+                       island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__);
                }
-               island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)");
        }
 
        BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
-               tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
+               MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
+               BMLoop *l;
 
                if (!uvedit_face_visible_test(scene, ima, efa, tf)) {
                        BM_elem_flag_disable(efa, BM_ELEM_TAG);
@@ -2735,14 +2994,25 @@ static void createTransUVs(bContext *C, TransInfo *t)
                        if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
                                countsel++;
 
-                               if (propconnected) {
+                               if (is_prop_connected || island_center) {
                                        UvElement *element = BM_uv_element_get(elementmap, efa, l);
-                                       BLI_BITMAP_ENABLE(island_enabled, element->island);
-                               }
 
+                                       if (is_prop_connected) {
+                                               BLI_BITMAP_ENABLE(island_enabled, element->island);
+                                       }
+
+                                       if (is_island_center) {
+                                               if (element->flag == false) {
+                                                       MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+                                                       add_v2_v2(island_center[element->island].co, luv->uv);
+                                                       island_center[element->island].co_num++;
+                                                       element->flag = true;
+                                               }
+                                       }
+                               }
                        }
 
-                       if (propmode) {
+                       if (is_prop_edit) {
                                count++;
                        }
                }
@@ -2750,13 +3020,19 @@ static void createTransUVs(bContext *C, TransInfo *t)
 
        /* note: in prop mode we need at least 1 selected */
        if (countsel == 0) {
-               if (propconnected) {
-                       MEM_freeN(island_enabled);
+               goto finally;
+       }
+
+       if (is_island_center) {
+               int i;
+
+               for (i = 0; i < elementmap->totalIslands; i++) {
+                       mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num);
+                       mul_v2_v2(island_center[i].co, t->aspect);
                }
-               return;
        }
 
-       t->total = (propmode) ? count : countsel;
+       t->total = (is_prop_edit) ? count : countsel;
        t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(UV Editing)");
        /* for each 2d uv coord a 3d vector is allocated, so that they can be
         * treated just as if they were 3d verts */
@@ -2769,56 +3045,88 @@ static void createTransUVs(bContext *C, TransInfo *t)
        td2d = t->data2d;
 
        BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+               BMLoop *l;
+
                if (!BM_elem_flag_test(efa, BM_ELEM_TAG))
                        continue;
 
                BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
-                       if (!propmode && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset))
+                       const bool selected = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
+                       MLoopUV *luv;
+                       const float *center = NULL;
+
+                       if (!is_prop_edit && !selected)
                                continue;
 
-                       if (propconnected) {
+                       if (is_prop_connected || is_island_center) {
                                UvElement *element = BM_uv_element_get(elementmap, efa, l);
-                               if (!BLI_BITMAP_TEST(island_enabled, element->island)) {
-                                       count_rejected++;
-                                       continue;
+
+                               if (is_prop_connected) {
+                                       if (!BLI_BITMAP_TEST(island_enabled, element->island)) {
+                                               count_rejected++;
+                                               continue;
+                                       }
+                               }
+
+                               if (is_island_center) {
+                                       center = island_center[element->island].co;
                                }
                        }
-                       
+
+                       BM_elem_flag_enable(l, BM_ELEM_TAG);
                        luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-                       UVsToTransData(sima, td++, td2d++, luv->uv, uvedit_uv_select_test(scene, l, cd_loop_uv_offset));
+                       UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, selected);
                }
        }
 
-       if (propconnected) {
+       if (is_prop_connected) {
                t->total -= count_rejected;
-               BM_uv_element_map_free(elementmap);
-               MEM_freeN(island_enabled);
        }
 
        if (sima->flag & SI_LIVE_UNWRAP)
                ED_uvedit_live_unwrap_begin(t->scene, t->obedit);
+
+
+finally:
+       if (is_prop_connected || is_island_center) {
+               BM_uv_element_map_free(elementmap);
+
+               if (is_prop_connected) {
+                       MEM_freeN(island_enabled);
+               }
+
+               if (island_center) {
+                       MEM_freeN(island_center);
+               }
+       }
 }
 
 void flushTransUVs(TransInfo *t)
 {
        SpaceImage *sima = t->sa->spacedata.first;
        TransData2D *td;
-       int a, width, height;
-       float aspx, aspy, invx, invy;
+       int a;
+       float aspect_inv[2], size[2];
+       const bool use_pixel_snap = ((sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL));
+
+       aspect_inv[0] = 1.0f / t->aspect[0];
+       aspect_inv[1] = 1.0f / t->aspect[1];
 
-       ED_space_image_get_uv_aspect(sima, &aspx, &aspy);
-       ED_space_image_get_size(sima, &width, &height);
-       invx = 1.0f / aspx;
-       invy = 1.0f / aspy;
+       if (use_pixel_snap) {
+               int size_i[2];
+               ED_space_image_get_size(sima, &size_i[0], &size_i[1]);
+               size[0] = size_i[0];
+               size[1] = size_i[1];
+       }
 
        /* flush to 2d vector from internally used 3d vector */
        for (a = 0, td = t->data2d; a < t->total; a++, td++) {
-               td->loc2d[0] = td->loc[0] * invx;
-               td->loc2d[1] = td->loc[1] * invy;
+               td->loc2d[0] = td->loc[0] * aspect_inv[0];
+               td->loc2d[1] = td->loc[1] * aspect_inv[1];
 
-               if ((sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL)) {
-                       td->loc2d[0] = roundf(width * td->loc2d[0]) / width;
-                       td->loc2d[1] = roundf(height * td->loc2d[1]) / height;
+               if (use_pixel_snap) {
+                       td->loc2d[0] = roundf(td->loc2d[0] * size[0]) / size[0];
+                       td->loc2d[1] = roundf(td->loc2d[1] * size[1]) / size[1];
                }
        }
 }
@@ -2826,44 +3134,45 @@ void flushTransUVs(TransInfo *t)
 bool clipUVTransform(TransInfo *t, float vec[2], const bool resize)
 {
        TransData *td;
-       int a, clipx = 1, clipy = 1;
-       float aspx, aspy, min[2], max[2];
+       int a;
+       bool clipx = true, clipy = true;
+       float min[2], max[2];
 
-       ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
        min[0] = min[1] = 0.0f;
-       max[0] = aspx; max[1] = aspy;
+       max[0] = t->aspect[0];
+       max[1] = t->aspect[1];
 
        for (a = 0, td = t->data; a < t->total; a++, td++) {
                minmax_v2v2_v2(min, max, td->loc);
        }
 
        if (resize) {
-               if (min[0] < 0.0f && t->center[0] > 0.0f && t->center[0] < aspx * 0.5f)
+               if (min[0] < 0.0f && t->center[0] > 0.0f && t->center[0] < t->aspect[0] * 0.5f)
                        vec[0] *= t->center[0] / (t->center[0] - min[0]);
-               else if (max[0] > aspx && t->center[0] < aspx)
-                       vec[0] *= (t->center[0] - aspx) / (t->center[0] - max[0]);
+               else if (max[0] > t->aspect[0] && t->center[0] < t->aspect[0])
+                       vec[0] *= (t->center[0] - t->aspect[0]) / (t->center[0] - max[0]);
                else
                        clipx = 0;
 
-               if (min[1] < 0.0f && t->center[1] > 0.0f && t->center[1] < aspy * 0.5f)
+               if (min[1] < 0.0f && t->center[1] > 0.0f && t->center[1] < t->aspect[1] * 0.5f)
                        vec[1] *= t->center[1] / (t->center[1] - min[1]);
-               else if (max[1] > aspy && t->center[1] < aspy)
-                       vec[1] *= (t->center[1] - aspy) / (t->center[1] - max[1]);
+               else if (max[1] > t->aspect[1] && t->center[1] < t->aspect[1])
+                       vec[1] *= (t->center[1] - t->aspect[1]) / (t->center[1] - max[1]);
                else
                        clipy = 0;
        }
        else {
                if (min[0] < 0.0f)
                        vec[0] -= min[0];
-               else if (max[0] > aspx)
-                       vec[0] -= max[0] - aspx;
+               else if (max[0] > t->aspect[0])
+                       vec[0] -= max[0] - t->aspect[0];
                else
                        clipx = 0;
 
                if (min[1] < 0.0f)
                        vec[1] -= min[1];
-               else if (max[1] > aspy)
-                       vec[1] -= max[1] - aspy;
+               else if (max[1] > t->aspect[1])
+                       vec[1] -= max[1] - t->aspect[1];
                else
                        clipy = 0;
        }
@@ -2875,9 +3184,6 @@ void clipUVData(TransInfo *t)
 {
        TransData *td = NULL;
        int a;
-       float aspx, aspy;
-
-       ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy);
 
        for (a = 0, td = t->data; a < t->total; a++, td++) {
                if (td->flag & TD_NOACTION)
@@ -2886,8 +3192,8 @@ void clipUVData(TransInfo *t)
                if ((td->flag & TD_SKIP) || (!td->loc))
                        continue;
 
-               td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), aspx);
-               td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), aspy);
+               td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), t->aspect[0]);
+               td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), t->aspect[1]);
        }
 }
 
@@ -2914,44 +3220,44 @@ static void createTransNlaData(bContext *C, TransInfo *t)
        SpaceNla *snla = NULL;
        TransData *td = NULL;
        TransDataNla *tdn = NULL;
-       
+
        bAnimContext ac;
        ListBase anim_data = {NULL, NULL};
        bAnimListElem *ale;
        int filter;
-       
+
        int count = 0;
-       
+
        /* determine what type of data we are operating on */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return;
        snla = (SpaceNla *)ac.sl;
-       
+
        /* filter data */
        filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
        ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-       
+
        /* which side of the current frame should be allowed */
        if (t->mode == TFM_TIME_EXTEND) {
                /* only side on which mouse is gets transformed */
                float xmouse, ymouse;
-               
-               UI_view2d_region_to_view(&ac.ar->v2d, t->imval[0], t->imval[1], &xmouse, &ymouse);
+
+               UI_view2d_region_to_view(&ac.ar->v2d, t->mouse.imval[0], t->mouse.imval[1], &xmouse, &ymouse);
                t->frame_side = (xmouse > CFRA) ? 'R' : 'L';
        }
        else {
                /* normal transform - both sides of current frame are considered */
                t->frame_side = 'B';
        }
-       
+
        /* loop 1: count how many strips are selected (consider each strip as 2 points) */
        for (ale = anim_data.first; ale; ale = ale->next) {
                NlaTrack *nlt = (NlaTrack *)ale->data;
                NlaStrip *strip;
-               
+
                /* make some meta-strips for chains of selected strips */
                BKE_nlastrips_make_metas(&nlt->strips, 1);
-               
+
                /* only consider selected strips */
                for (strip = nlt->strips.first; strip; strip = strip->next) {
                        // TODO: we can make strips have handles later on...
@@ -2964,31 +3270,30 @@ static void createTransNlaData(bContext *C, TransInfo *t)
                        }
                }
        }
-       
+
        /* stop if trying to build list if nothing selected */
        if (count == 0) {
-               /* clear temp metas that may have been created but aren't needed now 
+               /* clear temp metas that may have been created but aren't needed now
                 * because they fell on the wrong side of CFRA
                 */
                for (ale = anim_data.first; ale; ale = ale->next) {
                        NlaTrack *nlt = (NlaTrack *)ale->data;
                        BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
                }
-               
+
                /* cleanup temp list */
                ANIM_animdata_freelist(&anim_data);
                return;
        }
-       
+
        /* allocate memory for data */
        t->total = count;
-       
+
        t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(NLA Editor)");
        td = t->data;
-       t->customData = MEM_callocN(t->total * sizeof(TransDataNla), "TransDataNla (NLA Editor)");
-       tdn = t->customData;
-       t->flag |= T_FREE_CUSTOMDATA;
-       
+       t->custom.type.data = tdn = MEM_callocN(t->total * sizeof(TransDataNla), "TransDataNla (NLA Editor)");
+       t->custom.type.use_free = true;
+
        /* loop 2: build transdata array */
        for (ale = anim_data.first; ale; ale = ale->next) {
                /* only if a real NLA-track */
@@ -2996,7 +3301,7 @@ static void createTransNlaData(bContext *C, TransInfo *t)
                        AnimData *adt = ale->adt;
                        NlaTrack *nlt = (NlaTrack *)ale->data;
                        NlaStrip *strip;
-                       
+
                        /* only consider selected strips */
                        for (strip = nlt->strips.first; strip; strip = strip->next) {
                                // TODO: we can make strips have handles later on...
@@ -3013,44 +3318,44 @@ static void createTransNlaData(bContext *C, TransInfo *t)
                                                 *        cases, there will need to be 1 of these tdn elements in the array skipped...
                                                 */
                                                float center[3], yval;
-                                               
+
                                                /* firstly, init tdn settings */
                                                tdn->id = ale->id;
                                                tdn->oldTrack = tdn->nlt = nlt;
                                                tdn->strip = strip;
                                                tdn->trackIndex = BLI_findindex(&adt->nla_tracks, nlt);
-                                               
+
                                                yval = (float)(tdn->trackIndex * NLACHANNEL_STEP(snla));
-                                               
+
                                                tdn->h1[0] = strip->start;
                                                tdn->h1[1] = yval;
                                                tdn->h2[0] = strip->end;
                                                tdn->h2[1] = yval;
-                                               
+
                                                center[0] = (float)CFRA;
                                                center[1] = yval;
                                                center[2] = 0.0f;
-                                               
+
                                                /* set td's based on which handles are applicable */
                                                if (FrameOnMouseSide(t->frame_side, strip->start, (float)CFRA)) {
                                                        /* just set tdn to assume that it only has one handle for now */
                                                        tdn->handle = -1;
-                                                       
+
                                                        /* now, link the transform data up to this data */
                                                        if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) {
                                                                td->loc = tdn->h1;
                                                                copy_v3_v3(td->iloc, tdn->h1);
-                                                               
+
                                                                /* store all the other gunk that is required by transform */
                                                                copy_v3_v3(td->center, center);
                                                                memset(td->axismtx, 0, sizeof(td->axismtx));
                                                                td->axismtx[2][2] = 1.0f;
-                                                               
+
                                                                td->ext = NULL; td->val = NULL;
-                                                               
+
                                                                td->flag |= TD_SELECTED;
                                                                td->dist = 0.0f;
-                                                               
+
                                                                unit_m3(td->mtx);
                                                                unit_m3(td->smtx);
                                                        }
@@ -3059,29 +3364,29 @@ static void createTransNlaData(bContext *C, TransInfo *t)
                                                                td->val = &tdn->h1[0];
                                                                td->ival = tdn->h1[0];
                                                        }
-                                                       
+
                                                        td->extra = tdn;
                                                        td++;
                                                }
                                                if (FrameOnMouseSide(t->frame_side, strip->end, (float)CFRA)) {
                                                        /* if tdn is already holding the start handle, then we're doing both, otherwise, only end */
                                                        tdn->handle = (tdn->handle) ? 2 : 1;
-                                                       
+
                                                        /* now, link the transform data up to this data */
                                                        if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) {
                                                                td->loc = tdn->h2;
                                                                copy_v3_v3(td->iloc, tdn->h2);
-                                                               
+
                                                                /* store all the other gunk that is required by transform */
                                                                copy_v3_v3(td->center, center);
                                                                memset(td->axismtx, 0, sizeof(td->axismtx));
                                                                td->axismtx[2][2] = 1.0f;
-                                                               
+
                                                                td->ext = NULL; td->val = NULL;
-                                                               
+
                                                                td->flag |= TD_SELECTED;
                                                                td->dist = 0.0f;
-                                                               
+
                                                                unit_m3(td->mtx);
                                                                unit_m3(td->smtx);
                                                        }
@@ -3090,11 +3395,11 @@ static void createTransNlaData(bContext *C, TransInfo *t)
                                                                td->val = &tdn->h2[0];
                                                                td->ival = tdn->h2[0];
                                                        }
-                                                       
+
                                                        td->extra = tdn;
                                                        td++;
                                                }
-                                               
+
                                                /* if both handles were used, skip the next tdn (i.e. leave it blank) since the counting code is dumb...
                                                 * otherwise, just advance to the next one...
                                                 */
@@ -3107,7 +3412,7 @@ static void createTransNlaData(bContext *C, TransInfo *t)
                        }
                }
        }
-       
+
        /* cleanup temp list */
        ANIM_animdata_freelist(&anim_data);
 }
@@ -3156,18 +3461,18 @@ static int masklay_shape_cmp_frame(void *thunk, const void *a, const void *b)
 static void posttrans_gpd_clean(bGPdata *gpd)
 {
        bGPDlayer *gpl;
-       
+
        for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
                bGPDframe *gpf, *gpfn;
                bool is_double = false;
 
-               BLI_listbase_sort_r(&gpl->frames, &is_double, gpf_cmp_frame);
+               BLI_listbase_sort_r(&gpl->frames, gpf_cmp_frame, &is_double);
 
                if (is_double) {
                        for (gpf = gpl->frames.first; gpf; gpf = gpfn) {
                                gpfn = gpf->next;
                                if (gpfn && gpf->framenum == gpfn->framenum) {
-                                       gpencil_layer_delframe(gpl, gpf);
+                                       BKE_gpencil_layer_delframe(gpl, gpf);
                                }
                        }
                }
@@ -3188,7 +3493,7 @@ static void posttrans_mask_clean(Mask *mask)
                MaskLayerShape *masklay_shape, *masklay_shape_next;
                bool is_double = false;
 
-               BLI_listbase_sort_r(&masklay->splines_shapes, &is_double, masklay_shape_cmp_frame);
+               BLI_listbase_sort_r(&masklay->splines_shapes, masklay_shape_cmp_frame, &is_double);
 
                if (is_double) {
                        for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape_next) {
@@ -3207,65 +3512,134 @@ static void posttrans_mask_clean(Mask *mask)
        }
 }
 
+/* Time + Average value */
+typedef struct tRetainedKeyframe {
+       struct tRetainedKeyframe *next, *prev;
+       float frame;      /* frame to cluster around */
+       float val;        /* average value */
+
+       size_t tot_count; /* number of keyframes that have been averaged */
+       size_t del_count; /* number of keyframes of this sort that have been deleted so far */
+} tRetainedKeyframe;
+
 /* Called during special_aftertrans_update to make sure selected keyframes replace
  * any other keyframes which may reside on that frame (that is not selected).
  */
-static void posttrans_fcurve_clean(FCurve *fcu, const short use_handle)
+static void posttrans_fcurve_clean(FCurve *fcu, const bool use_handle)
 {
-       float *selcache;    /* cache for frame numbers of selected frames (fcu->totvert*sizeof(float)) */
-       int len, index, i;  /* number of frames in cache, item index */
+       /* NOTE: We assume that all keys are sorted */
+       ListBase retained_keys = {NULL, NULL};
+       const bool can_average_points = ((fcu->flag & (FCURVE_INT_VALUES | FCURVE_DISCRETE_VALUES)) == 0);
 
-       /* allocate memory for the cache */
-       // TODO: investigate using BezTriple columns instead?
-       if (fcu->totvert == 0 || fcu->bezt == NULL)
+       /* sanity checks */
+       if ((fcu->totvert == 0) || (fcu->bezt == NULL))
                return;
-       selcache = MEM_callocN(sizeof(float) * fcu->totvert, "FCurveSelFrameNums");
-       len = 0;
-       index = 0;
 
-       /* We do 2 loops, 1 for marking keyframes for deletion, one for deleting
-        * as there is no guarantee what order the keyframes are exactly, even though
-        * they have been sorted by time.
+       /* 1) Identify selected keyframes, and average the values on those
+        * in case there are collisions due to multiple keys getting scaled
+        * to all end up on the same frame
         */
-
-       /*      Loop 1: find selected keyframes   */
-       for (i = 0; i < fcu->totvert; i++) {
+       for (int i = 0; i < fcu->totvert; i++) {
                BezTriple *bezt = &fcu->bezt[i];
-               
-               if (BEZSELECTED(bezt)) {
-                       selcache[index] = bezt->vec[1][0];
-                       index++;
-                       len++;
+
+               if (BEZT_ISSEL_ANY(bezt)) {
+                       bool found = false;
+
+                       /* If there's another selected frame here, merge it */
+                       for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) {
+                               if (IS_EQT(rk->frame, bezt->vec[1][0], BEZT_BINARYSEARCH_THRESH)) {
+                                       rk->val += bezt->vec[1][1];
+                                       rk->tot_count++;
+
+                                       found = true;
+                                       break;
+                               }
+                               else if (rk->frame < bezt->vec[1][0]) {
+                                       /* Terminate early if have passed the supposed insertion point? */
+                                       break;
+                               }
+                       }
+
+                       /* If nothing found yet, create a new one */
+                       if (found == false) {
+                               tRetainedKeyframe *rk = MEM_callocN(sizeof(tRetainedKeyframe), "tRetainedKeyframe");
+
+                               rk->frame = bezt->vec[1][0];
+                               rk->val   = bezt->vec[1][1];
+                               rk->tot_count = 1;
+
+                               BLI_addtail(&retained_keys, rk);
+                       }
                }
        }
 
-       /* Loop 2: delete unselected keyframes on the same frames 
-        * (if any keyframes were found, or the whole curve wasn't affected) 
+       if (BLI_listbase_is_empty(&retained_keys)) {
+               /* This may happen if none of the points were selected... */
+               if (G.debug & G_DEBUG) {
+                       printf("%s: nothing to do for FCurve %p (rna_path = '%s')\n", __func__, fcu, fcu->rna_path);
+               }
+               return;
+       }
+       else {
+               /* Compute the average values for each retained keyframe */
+               for (tRetainedKeyframe *rk = retained_keys.first; rk; rk = rk->next) {
+                       rk->val = rk->val / (float)rk->tot_count;
+               }
+       }
+
+       /* 2) Delete all keyframes duplicating the "retained keys" found above
+        *   - Most of these will be unselected keyframes
+        *   - Some will be selected keyframes though. For those, we only keep the last one
+        *     (or else everything is gone), and replace its value with the averaged value.
         */
-       if ((len) && (len != fcu->totvert)) {
-               for (i = fcu->totvert - 1; i >= 0; i--) {
-                       BezTriple *bezt = &fcu->bezt[i];
-                       
-                       if (BEZSELECTED(bezt) == 0) {
-                               /* check beztriple should be removed according to cache */
-                               for (index = 0; index < len; index++) {
-                                       if (IS_EQF(bezt->vec[1][0], selcache[index])) {
+       for (int i = fcu->totvert - 1; i >= 0; i--) {
+               BezTriple *bezt = &fcu->bezt[i];
+
+               /* Is this keyframe a candidate for deletion? */
+               /* TODO: Replace loop with an O(1) lookup instead */
+               for (tRetainedKeyframe *rk = retained_keys.last; rk; rk = rk->prev) {
+                       if (IS_EQT(bezt->vec[1][0], rk->frame, BEZT_BINARYSEARCH_THRESH)) {
+                               /* Selected keys are treated with greater care than unselected ones... */
+                               if (BEZT_ISSEL_ANY(bezt)) {
+                                       /* - If this is the last selected key left (based on rk->del_count) ==> UPDATE IT
+                                        *   (or else we wouldn't have any keyframe left here)
+                                        * - Otherwise, there are still other selected keyframes on this frame
+                                        *   to be merged down still ==> DELETE IT
+                                        */
+                                       if (rk->del_count == rk->tot_count - 1) {
+                                               /* Update keyframe... */
+                                               if (can_average_points) {
+                                                       /* TODO: update handles too? */
+                                                       bezt->vec[1][1] = rk->val;
+                                               }
+                                       }
+                                       else {
+                                               /* Delete Keyframe */
                                                delete_fcurve_key(fcu, i, 0);
-                                               break;
                                        }
-                                       else if (bezt->vec[1][0] < selcache[index])
-                                               break;
+
+                                       /* Update count of how many we've deleted
+                                        * - It should only matter that we're doing this for all but the last one
+                                        */
+                                       rk->del_count++;
                                }
+                               else {
+                                       /* Always delete - Unselected keys don't matter */
+                                       delete_fcurve_key(fcu, i, 0);
+                               }
+
+                               /* Stop the RK search... we've found our match now */
+                               break;
                        }
                }
-               
-               testhandles_fcurve(fcu, use_handle);
        }
 
-       /* free cache */
-       MEM_freeN(selcache);
-}
+       /* 3) Recalculate handles */
+       testhandles_fcurve(fcu, use_handle);
 
+       /* cleanup */
+       BLI_freelistN(&retained_keys);
+}
 
 
 /* Called by special_aftertrans_update to make sure selected keyframes replace
@@ -3287,11 +3661,11 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act)
         */
        for (ale = anim_data.first; ale; ale = ale->next) {
                AnimData *adt = ANIM_nla_mapping_get(ac, ale);
-               
+
                if (adt) {
-                       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1);
+                       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
                        posttrans_fcurve_clean(ale->key_data, false); /* only use handles in graph editor */
-                       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
+                       ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
                }
                else
                        posttrans_fcurve_clean(ale->key_data, false);  /* only use handles in graph editor */
@@ -3304,76 +3678,90 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act)
 /* ----------------------------- */
 
 /* fully select selected beztriples, but only include if it's on the right side of cfra */
-static int count_fcurve_keys(FCurve *fcu, char side, float cfra)
+static int count_fcurve_keys(FCurve *fcu, char side, float cfra, bool is_prop_edit)
 {
        BezTriple *bezt;
-       int i, count = 0;
+       int i, count = 0, count_all = 0;
 
        if (ELEM(NULL, fcu, fcu->bezt))
                return count;
 
        /* only include points that occur on the right side of cfra */
        for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
-               if (bezt->f2 & SELECT) {
+               if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) {
                        /* no need to adjust the handle selection since they are assumed
                         * selected (like graph editor with SIPO_NOHANDLES) */
-                       if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) {
-                               count += 1;
-                       }
+                       if (bezt->f2 & SELECT)
+                               count++;
+
+                       count_all++;
                }
        }
 
-       return count;
+       if (is_prop_edit && count > 0)
+               return count_all;
+       else return count;
 }
 
 /* fully select selected beztriples, but only include if it's on the right side of cfra */
-static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra)
+static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra, bool is_prop_edit)
 {
        bGPDframe *gpf;
-       int count = 0;
-       
+       int count = 0, count_all = 0;
+
        if (gpl == NULL)
                return count;
-       
+
        /* only include points that occur on the right side of cfra */
        for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
-               if (gpf->flag & GP_FRAME_SELECT) {
-                       if (FrameOnMouseSide(side, (float)gpf->framenum, cfra))
+               if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) {
+                       if (gpf->flag & GP_FRAME_SELECT)
                                count++;
+                       count_all++;
                }
        }
-       
-       return count;
+
+       if (is_prop_edit && count > 0)
+               return count_all;
+       else
+               return count;
 }
 
 /* fully select selected beztriples, but only include if it's on the right side of cfra */
-static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra)
+static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra, bool is_prop_edit)
 {
        MaskLayerShape *masklayer_shape;
-       int count = 0;
+       int count = 0, count_all = 0;
 
        if (masklay == NULL)
                return count;
 
        /* only include points that occur on the right side of cfra */
        for (masklayer_shape = masklay->splines_shapes.first; masklayer_shape; masklayer_shape = masklayer_shape->next) {
-               if (masklayer_shape->flag & MASK_SHAPE_SELECT) {
-                       if (FrameOnMouseSide(side, (float)masklayer_shape->frame, cfra))
+               if (FrameOnMouseSide(side, (float)masklayer_shape->frame, cfra)) {
+                       if (masklayer_shape->flag & MASK_SHAPE_SELECT)
                                count++;
+                       count_all++;
                }
        }
 
-       return count;
+       if (is_prop_edit && count > 0)
+               return count_all;
+       else
+               return count;
 }
 
 
 /* This function assigns the information to transdata */
-static void TimeToTransData(TransData *td, float *time, AnimData *adt)
+static void TimeToTransData(TransData *td, float *time, AnimData *adt, float ypos)
 {
        /* memory is calloc'ed, so that should zero everything nicely for us */
        td->val = time;
        td->ival = *(time);
 
+       td->center[0] = td->ival;
+       td->center[1] = ypos;
+
        /* store the AnimData where this keyframe exists as a keyframe of the
         * active action as td->extra.
         */
@@ -3387,7 +3775,7 @@ static void TimeToTransData(TransData *td, float *time, AnimData *adt)
  * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data
  * on the named side are used.
  */
-static TransData *ActionFCurveToTransData(TransData *td, TransData2D **td2dv, FCurve *fcu, AnimData *adt, char side, float cfra)
+static TransData *ActionFCurveToTransData(TransData *td, TransData2D **td2dv, FCurve *fcu, AnimData *adt, char side, float cfra, bool is_prop_edit, float ypos)
 {
        BezTriple *bezt;
        TransData2D *td2d = *td2dv;
@@ -3398,27 +3786,30 @@ static TransData *ActionFCurveToTransData(TransData *td, TransData2D **td2dv, FC
 
        for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
                /* only add selected keyframes (for now, proportional edit is not enabled) */
-               if (bezt->f2 & SELECT) { /* note this MUST match count_fcurve_keys(), so can't use BEZSELECTED() macro */
+               if (is_prop_edit || (bezt->f2 & SELECT)) { /* note this MUST match count_fcurve_keys(), so can't use BEZT_ISSEL_ANY() macro */
                        /* only add if on the right 'side' of the current frame */
                        if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) {
-                               TimeToTransData(td, bezt->vec[1], adt);
-                               
+                               TimeToTransData(td, bezt->vec[1], adt, ypos);
+
+                               if (bezt->f2 & SELECT)
+                                       td->flag |= TD_SELECTED;
+
                                /*set flags to move handles as necessary*/
                                td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2;
                                td2d->h1 = bezt->vec[0];
                                td2d->h2 = bezt->vec[2];
-                               
+
                                copy_v2_v2(td2d->ih1, td2d->h1);
                                copy_v2_v2(td2d->ih2, td2d->h2);
-                               
+
                                td++;
                                td2d++;
                        }
                }
        }
-       
+
        *td2dv = td2d;
-       
+
        return td;
 }
 
@@ -3431,18 +3822,12 @@ typedef struct tGPFtransdata {
 /* This function helps flush transdata written to tempdata into the gp-frames  */
 void flushTransIntFrameActionData(TransInfo *t)
 {
-       tGPFtransdata *tfd;
+       tGPFtransdata *tfd = t->custom.type.data;
        int i;
 
-       /* find the first one to start from */
-       if (t->mode == TFM_TIME_SLIDE)
-               tfd = (tGPFtransdata *)((float *)(t->customData) + 2);
-       else
-               tfd = (tGPFtransdata *)(t->customData);
-
        /* flush data! */
        for (i = 0; i < t->total; i++, tfd++) {
-               *(tfd->sdata) = iroundf(tfd->val);
+               *(tfd->sdata) = round_fl_to_int(tfd->val);
        }
 }
 
@@ -3453,22 +3838,25 @@ void flushTransIntFrameActionData(TransInfo *t)
  * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data
  * on the named side are used.
  */
-static int GPLayerToTransData(TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, char side, float cfra)
+static int GPLayerToTransData(TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, char side, float cfra, bool is_prop_edit, float ypos)
 {
        bGPDframe *gpf;
        int count = 0;
-       
+
        /* check for select frames on right side of current frame */
        for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
-               if (gpf->flag & GP_FRAME_SELECT) {
+               if (is_prop_edit || (gpf->flag & GP_FRAME_SELECT)) {
                        if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) {
                                /* memory is calloc'ed, so that should zero everything nicely for us */
                                td->val = &tfd->val;
                                td->ival = (float)gpf->framenum;
-                               
+
+                               td->center[0] = td->ival;
+                               td->center[1] = ypos;
+
                                tfd->val = (float)gpf->framenum;
                                tfd->sdata = &gpf->framenum;
-                               
+
                                /* advance td now */
                                td++;
                                tfd++;
@@ -3476,24 +3864,27 @@ static int GPLayerToTransData(TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl,
                        }
                }
        }
-       
+
        return count;
 }
 
 /* refer to comment above #GPLayerToTransData, this is the same but for masks */
-static int MaskLayerToTransData(TransData *td, tGPFtransdata *tfd, MaskLayer *masklay, char side, float cfra)
+static int MaskLayerToTransData(TransData *td, tGPFtransdata *tfd, MaskLayer *masklay, char side, float cfra, bool is_prop_edit, float ypos)
 {
        MaskLayerShape *masklay_shape;
        int count = 0;
 
        /* check for select frames on right side of current frame */
        for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) {
-               if (masklay_shape->flag & MASK_SHAPE_SELECT) {
+               if (is_prop_edit || (masklay_shape->flag & MASK_SHAPE_SELECT)) {
                        if (FrameOnMouseSide(side, (float)masklay_shape->frame, cfra)) {
                                /* memory is calloc'ed, so that should zero everything nicely for us */
                                td->val = &tfd->val;
                                td->ival = (float)masklay_shape->frame;
 
+                               td->center[0] = td->ival;
+                               td->center[1] = ypos;
+
                                tfd->val = (float)masklay_shape->frame;
                                tfd->sdata = &masklay_shape->frame;
 
@@ -3515,43 +3906,53 @@ static void createTransActionData(bContext *C, TransInfo *t)
        TransData *td = NULL;
        TransData2D *td2d = NULL;
        tGPFtransdata *tfd = NULL;
-       
+
+       rcti *mask = &t->ar->v2d.mask;
+       rctf *datamask = &t->ar->v2d.cur;
+
+       float xsize = BLI_rctf_size_x(datamask);
+       float ysize = BLI_rctf_size_y(datamask);
+       float xmask = BLI_rcti_size_x(mask);
+       float ymask = BLI_rcti_size_y(mask);
+
        bAnimContext ac;
        ListBase anim_data = {NULL, NULL};
        bAnimListElem *ale;
        int filter;
-       
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
+
        int count = 0;
        float cfra;
-       
+       float ypos = 1.0f / ((ysize / xsize) * (xmask / ymask)) * BLI_rctf_cent_y(&t->ar->v2d.cur);
+
        /* determine what type of data we are operating on */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return;
-       
+
        /* filter data */
        if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT);
        else
                filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/);
        ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-       
+
        /* which side of the current frame should be allowed */
        if (t->mode == TFM_TIME_EXTEND) {
                /* only side on which mouse is gets transformed */
                float xmouse, ymouse;
-               
-               UI_view2d_region_to_view(&ac.ar->v2d, t->imval[0], t->imval[1], &xmouse, &ymouse);
+
+               UI_view2d_region_to_view(&ac.ar->v2d, t->mouse.imval[0], t->mouse.imval[1], &xmouse, &ymouse);
                t->frame_side = (xmouse > CFRA) ? 'R' : 'L'; // XXX use t->frame_side
        }
        else {
                /* normal transform - both sides of current frame are considered */
                t->frame_side = 'B';
        }
-       
+
        /* loop 1: fully select ipo-keys and count how many BezTriples are selected */
        for (ale = anim_data.first; ale; ale = ale->next) {
                AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
-               
+               int adt_count = 0;
                /* convert current-frame to action-time (slightly less accurate, especially under
                 * higher scaling ratios, but is faster than converting all points)
                 */
@@ -3559,56 +3960,63 @@ static void createTransActionData(bContext *C, TransInfo *t)
                        cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
                else
                        cfra = (float)CFRA;
-               
-               if (ale->type == ANIMTYPE_FCURVE)
-                       count += count_fcurve_keys(ale->key_data, t->frame_side, cfra);
+
+               if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE))
+                       adt_count = count_fcurve_keys(ale->key_data, t->frame_side, cfra, is_prop_edit);
                else if (ale->type == ANIMTYPE_GPLAYER)
-                       count += count_gplayer_frames(ale->data, t->frame_side, cfra);
+                       adt_count = count_gplayer_frames(ale->data, t->frame_side, cfra, is_prop_edit);
                else if (ale->type == ANIMTYPE_MASKLAYER)
-                       count += count_masklayer_frames(ale->data, t->frame_side, cfra);
+                       adt_count = count_masklayer_frames(ale->data, t->frame_side, cfra, is_prop_edit);
                else
                        BLI_assert(0);
+
+               if (adt_count > 0) {
+                       count += adt_count;
+                       ale->tag = true;
+               }
        }
-       
+
        /* stop if trying to build list if nothing selected */
        if (count == 0) {
                /* cleanup temp list */
                ANIM_animdata_freelist(&anim_data);
                return;
        }
-       
+
        /* allocate memory for data */
        t->total = count;
-       
+
        t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(Action Editor)");
        t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "transdata2d");
        td = t->data;
        td2d = t->data2d;
-       
+
        if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
-               if (t->mode == TFM_TIME_SLIDE) {
-                       t->customData = MEM_callocN((sizeof(float) * 2) + (sizeof(tGPFtransdata) * count), "TimeSlide + tGPFtransdata");
-                       t->flag |= T_FREE_CUSTOMDATA;
-                       tfd = (tGPFtransdata *)((float *)(t->customData) + 2);
-               }
-               else {
-                       t->customData = MEM_callocN(sizeof(tGPFtransdata) * count, "tGPFtransdata");
-                       t->flag |= T_FREE_CUSTOMDATA;
-                       tfd = (tGPFtransdata *)(t->customData);
-               }
-       }
-       else if (t->mode == TFM_TIME_SLIDE) {
-               t->customData = MEM_callocN(sizeof(float) * 2, "TimeSlide Min/Max");
-               t->flag |= T_FREE_CUSTOMDATA;
+               t->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * count, "tGPFtransdata");
+               t->custom.type.use_free = true;
        }
-       
+
        /* loop 2: build transdata array */
        for (ale = anim_data.first; ale; ale = ale->next) {
+
+               if (is_prop_edit && !ale->tag)
+                       continue;
+
+               cfra = (float)CFRA;
+
+               {
+                       AnimData *adt;
+                       adt = ANIM_nla_mapping_get(&ac, ale);
+                       if (adt) {
+                               cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
+                       }
+               }
+
                if (ale->type == ANIMTYPE_GPLAYER) {
                        bGPDlayer *gpl = (bGPDlayer *)ale->data;
                        int i;
-                       
-                       i = GPLayerToTransData(td, tfd, gpl, t->frame_side, cfra);
+
+                       i = GPLayerToTransData(td, tfd, gpl, t->frame_side, cfra, is_prop_edit, ypos);
                        td += i;
                        tfd += i;
                }
@@ -3616,50 +4024,120 @@ static void createTransActionData(bContext *C, TransInfo *t)
                        MaskLayer *masklay = (MaskLayer *)ale->data;
                        int i;
 
-                       i = MaskLayerToTransData(td, tfd, masklay, t->frame_side, cfra);
+                       i = MaskLayerToTransData(td, tfd, masklay, t->frame_side, cfra, is_prop_edit, ypos);
                        td += i;
                        tfd += i;
                }
                else {
                        AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
                        FCurve *fcu = (FCurve *)ale->key_data;
-                       
-                       /* convert current-frame to action-time (slightly less accurate, especially under
-                        * higher scaling ratios, but is faster than converting all points)
-                        */
+
+                       td = ActionFCurveToTransData(td, &td2d, fcu, adt, t->frame_side, cfra, is_prop_edit, ypos);
+               }
+       }
+
+       /* calculate distances for proportional editing */
+       if (is_prop_edit) {
+               td = t->data;
+
+               for (ale = anim_data.first; ale; ale = ale->next) {
+                       AnimData *adt;
+
+                       /* F-Curve may not have any keyframes */
+                       if (!ale->tag)
+                               continue;
+
+                       adt = ANIM_nla_mapping_get(&ac, ale);
                        if (adt)
                                cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
                        else
                                cfra = (float)CFRA;
-                       
-                       td = ActionFCurveToTransData(td, &td2d, fcu, adt, t->frame_side, cfra);
+
+                       if (ale->type == ANIMTYPE_GPLAYER) {
+                               bGPDlayer *gpl = (bGPDlayer *)ale->data;
+                               bGPDframe *gpf;
+
+                               for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+                                       if (gpf->flag & GP_FRAME_SELECT) {
+                                               td->dist = td->rdist = 0.0f;
+                                       }
+                                       else {
+                                               bGPDframe *gpf_iter;
+                                               int min = INT_MAX;
+                                               for (gpf_iter = gpl->frames.first; gpf_iter; gpf_iter = gpf->next) {
+                                                       if (gpf_iter->flag & GP_FRAME_SELECT) {
+                                                               if (FrameOnMouseSide(t->frame_side, (float)gpf_iter->framenum, cfra)) {
+                                                                       int val = abs(gpf->framenum - gpf_iter->framenum);
+                                                                       if (val < min) {
+                                                                               min = val;
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                               td->dist = td->rdist = min;
+                                       }
+                                       td++;
+                               }
+                       }
+                       else if (ale->type == ANIMTYPE_MASKLAYER) {
+                               MaskLayer *masklay = (MaskLayer *)ale->data;
+                               MaskLayerShape *masklay_shape;
+
+                               for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) {
+                                       if (FrameOnMouseSide(t->frame_side, (float)masklay_shape->frame, cfra)) {
+                                               if (masklay_shape->flag & MASK_SHAPE_SELECT) {
+                                                       td->dist = td->rdist = 0.0f;
+                                               }
+                                               else {
+                                                       MaskLayerShape *masklay_iter;
+                                                       int min = INT_MAX;
+                                                       for (masklay_iter = masklay->splines_shapes.first; masklay_iter; masklay_iter = masklay_iter->next) {
+                                                               if (masklay_iter->flag & MASK_SHAPE_SELECT) {
+                                                                       if (FrameOnMouseSide(t->frame_side, (float)masklay_iter->frame, cfra)) {
+                                                                               int val = abs(masklay_shape->frame - masklay_iter->frame);
+                                                                               if (val < min) {
+                                                                                       min = val;
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                                       td->dist = td->rdist = min;
+                                               }
+                                               td++;
+                                       }
+                               }
+                       }
+                       else {
+                               FCurve *fcu = (FCurve *)ale->key_data;
+                               BezTriple *bezt;
+                               int i;
+
+                               for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
+                                       if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
+                                               if (bezt->f2 & SELECT) {
+                                                       td->dist = td->rdist = 0.0f;
+                                               }
+                                               else {
+                                                       BezTriple *bezt_iter;
+                                                       int j;
+                                                       float min = FLT_MAX;
+                                                       for (j = 0, bezt_iter = fcu->bezt; j < fcu->totvert; j++, bezt_iter++) {
+                                                               if (bezt_iter->f2 & SELECT) {
+                                                                       if (FrameOnMouseSide(t->frame_side, (float)bezt_iter->vec[1][0], cfra)) {
+                                                                               float val = fabs(bezt->vec[1][0] - bezt_iter->vec[1][0]);
+                                                                               if (val < min)
+                                                                                       min = val;
+                                                                       }
+                                                               }
+                                                       }
+                                                       td->dist = td->rdist = min;
+                                               }
+                                               td++;
+                                       }
+                               }
+                       }
                }
        }
-       
-       /* check if we're supposed to be setting minx/maxx for TimeSlide */
-       if (t->mode == TFM_TIME_SLIDE) {
-               float min = 999999999.0f, max = -999999999.0f;
-               int i;
-               
-               td = t->data;
-               for (i = 0; i < count; i++, td++) {
-                       if (min > *(td->val)) min = *(td->val);
-                       if (max < *(td->val)) max = *(td->val);
-               }
-               
-               if (min == max) {
-                       /* just use the current frame ranges */
-                       min = (float)PSFRA;
-                       max = (float)PEFRA;
-               }
-               
-               /* minx/maxx values used by TimeSlide are stored as a
-                * calloced 2-float array in t->customData. This gets freed
-                * in postTrans (T_FREE_CUSTOMDATA).
-                */
-               *((float *)(t->customData)) = min;
-               *((float *)(t->customData) + 1) = max;
-       }
 
        /* cleanup temp list */
        ANIM_animdata_freelist(&anim_data);
@@ -3669,6 +4147,7 @@ static void createTransActionData(bContext *C, TransInfo *t)
 
 typedef struct TransDataGraph {
        float unit_scale;
+       float offset;
 } TransDataGraph;
 
 /* Helper function for createTransGraphEditData, which is responsible for associating
@@ -3677,7 +4156,7 @@ typedef struct TransDataGraph {
 static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph *tdg,
                               AnimData *adt, BezTriple *bezt,
                               int bi, bool selected, bool ishandle, bool intvals,
-                              float mtx[3][3], float smtx[3][3], float unit_scale)
+                              float mtx[3][3], float smtx[3][3], float unit_scale, float offset)
 {
        float *loc = bezt->vec[bi];
        const float *cent = bezt->vec[1];
@@ -3688,29 +4167,29 @@ static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph *
         * Due to NLA mapping, we apply NLA mapping to some of the verts here,
         * and then that mapping will be undone after transform is done.
         */
-       
+
        if (adt) {
                td2d->loc[0] = BKE_nla_tweakedit_remap(adt, loc[0], NLATIME_CONVERT_MAP);
-               td2d->loc[1] = loc[1] * unit_scale;
+               td2d->loc[1] = (loc[1] + offset) * unit_scale;
                td2d->loc[2] = 0.0f;
                td2d->loc2d = loc;
-               
+
                td->loc = td2d->loc;
                td->center[0] = BKE_nla_tweakedit_remap(adt, cent[0], NLATIME_CONVERT_MAP);
-               td->center[1] = cent[1] * unit_scale;
+               td->center[1] = (cent[1] + offset) * unit_scale;
                td->center[2] = 0.0f;
-               
+
                copy_v3_v3(td->iloc, td->loc);
        }
        else {
                td2d->loc[0] = loc[0];
-               td2d->loc[1] = loc[1] * unit_scale;
+               td2d->loc[1] = (loc[1] + offset) * unit_scale;
                td2d->loc[2] = 0.0f;
                td2d->loc2d = loc;
-               
+
                td->loc = td2d->loc;
                copy_v3_v3(td->center, cent);
-               td->center[1] *= unit_scale;
+               td->center[1] = (td->center[1] + offset) * unit_scale;
                copy_v3_v3(td->iloc, td->loc);
        }
 
@@ -3727,19 +4206,19 @@ static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph *
 
        memset(td->axismtx, 0, sizeof(td->axismtx));
        td->axismtx[2][2] = 1.0f;
-       
+
        td->ext = NULL; td->val = NULL;
-       
+
        /* store AnimData info in td->extra, for applying mapping when flushing */
        td->extra = adt;
-       
+
        if (selected) {
                td->flag |= TD_SELECTED;
                td->dist = 0.0f;
        }
        else
                td->dist = FLT_MAX;
-       
+
        if (ishandle)
                td->flag |= TD_NOTIMESNAP;
        if (intvals)
@@ -3750,6 +4229,7 @@ static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph *
        copy_m3_m3(td->smtx, smtx);
 
        tdg->unit_scale = unit_scale;
+       tdg->offset = offset;
 }
 
 static bool graph_edit_is_translation_mode(TransInfo *t)
@@ -3759,7 +4239,31 @@ static bool graph_edit_is_translation_mode(TransInfo *t)
 
 static bool graph_edit_use_local_center(TransInfo *t)
 {
-       return (t->around == V3D_LOCAL) && !graph_edit_is_translation_mode(t);
+       return ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
+               (graph_edit_is_translation_mode(t) == false));
+}
+
+
+static void graph_key_shortest_dist(TransInfo *t, FCurve *fcu, TransData *td_start, TransData *td, int cfra, bool use_handle)
+{
+       int j = 0;
+       TransData *td_iter = td_start;
+
+       td->dist = FLT_MAX;
+       for (; j < fcu->totvert; j++) {
+               BezTriple *bezt = fcu->bezt + j;
+               if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
+                       const bool sel2 = (bezt->f2 & SELECT) != 0;
+                       const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2;
+                       const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2;
+
+                       if (sel1 || sel2 || sel3) {
+                               td->dist = td->rdist = min_ff(td->dist, fabs(td_iter->center[0] - td->center[0]));
+                       }
+
+                       td_iter += 3;
+               }
+       }
 }
 
 static void createTransGraphEditData(bContext *C, TransInfo *t)
@@ -3768,24 +4272,25 @@ static void createTransGraphEditData(bContext *C, TransInfo *t)
        Scene *scene = t->scene;
        ARegion *ar = t->ar;
        View2D *v2d = &ar->v2d;
-       
+
        TransData *td = NULL;
        TransData2D *td2d = NULL;
        TransDataGraph *tdg = NULL;
-       
+
        bAnimContext ac;
        ListBase anim_data = {NULL, NULL};
        bAnimListElem *ale;
        int filter;
-       
+
        BezTriple *bezt;
        int count = 0, i;
        float mtx[3][3], smtx[3][3];
        const bool is_translation_mode = graph_edit_is_translation_mode(t);
        const bool use_handle = !(sipo->flag & SIPO_NOHANDLES);
        const bool use_local_center = graph_edit_use_local_center(t);
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
        short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS;
-       
+
        /* determine what type of data we are operating on */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return;
@@ -3795,26 +4300,28 @@ static void createTransGraphEditData(bContext *C, TransInfo *t)
        /* filter data */
        filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
        ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-       
+
        /* which side of the current frame should be allowed */
        // XXX we still want this mode, but how to get this using standard transform too?
        if (t->mode == TFM_TIME_EXTEND) {
                /* only side on which mouse is gets transformed */
                float xmouse, ymouse;
-               
-               UI_view2d_region_to_view(v2d, t->imval[0], t->imval[1], &xmouse, &ymouse);
+
+               UI_view2d_region_to_view(v2d, t->mouse.imval[0], t->mouse.imval[1], &xmouse, &ymouse);
                t->frame_side = (xmouse > CFRA) ? 'R' : 'L'; // XXX use t->frame_side
        }
        else {
                /* normal transform - both sides of current frame are considered */
                t->frame_side = 'B';
        }
-       
+
        /* loop 1: count how many BezTriples (specifically their verts) are selected (or should be edited) */
        for (ale = anim_data.first; ale; ale = ale->next) {
                AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
                FCurve *fcu = (FCurve *)ale->key_data;
                float cfra;
+               int curvecount = 0;
+               bool selected = false;
 
                /* F-Curve may not have any keyframes */
                if (fcu->bezt == NULL)
@@ -3835,73 +4342,87 @@ static void createTransGraphEditData(bContext *C, TransInfo *t)
                                const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2;
                                const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2;
 
-                               if (!is_translation_mode || !(sel2)) {
-                                       if (sel1) {
-                                               count++;
+                               if (is_prop_edit) {
+                                       curvecount += 3;
+                                       if (sel2 || sel1 || sel3)
+                                               selected = true;
+                               }
+                               else {
+                                       if (!is_translation_mode || !(sel2)) {
+                                               if (sel1) {
+                                                       count++;
+                                               }
+
+                                               if (sel3) {
+                                                       count++;
+                                               }
                                        }
 
-                                       if (sel3) {
+                                       /* only include main vert if selected */
+                                       if (sel2 && !use_local_center) {
                                                count++;
                                        }
                                }
+                       }
+               }
 
-                               /* only include main vert if selected */
-                               if (sel2 && !use_local_center) {
-                                       count++;
-                               }
+               if (is_prop_edit) {
+                       if (selected) {
+                               count += curvecount;
+                               ale->tag = true;
                        }
                }
        }
-       
+
        /* stop if trying to build list if nothing selected */
        if (count == 0) {
                /* cleanup temp list */
                ANIM_animdata_freelist(&anim_data);
                return;
        }
-       
+
        /* allocate memory for data */
        t->total = count;
-       
+
        t->data = MEM_callocN(t->total * sizeof(TransData), "TransData (Graph Editor)");
        /* for each 2d vert a 3d vector is allocated, so that they can be treated just as if they were 3d verts */
        t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransData2D (Graph Editor)");
-       t->customData = MEM_callocN(t->total * sizeof(TransDataGraph), "TransDataGraph");
-       t->flag |= T_FREE_CUSTOMDATA;
-       
+       t->custom.type.data = MEM_callocN(t->total * sizeof(TransDataGraph), "TransDataGraph");
+       t->custom.type.use_free = true;
+
        td = t->data;
        td2d = t->data2d;
-       tdg = t->customData;
-       
+       tdg = t->custom.type.data;
+
        /* precompute space-conversion matrices for dealing with non-uniform scaling of Graph Editor */
        unit_m3(mtx);
        unit_m3(smtx);
-       
+
        if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) {
                float xscale, yscale;
-               
+
                /* apply scale factors to x and y axes of space-conversion matrices */
                UI_view2d_scale_get(v2d, &xscale, &yscale);
-               
+
                /* mtx is data to global (i.e. view) conversion */
                mul_v3_fl(mtx[0], xscale);
                mul_v3_fl(mtx[1], yscale);
-               
+
                /* smtx is global (i.e. view) to data conversion */
                if (IS_EQF(xscale, 0.0f) == 0) mul_v3_fl(smtx[0], 1.0f / xscale);
                if (IS_EQF(yscale, 0.0f) == 0) mul_v3_fl(smtx[1], 1.0f / yscale);
        }
-       
+
        /* loop 2: build transdata arrays */
        for (ale = anim_data.first; ale; ale = ale->next) {
                AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
                FCurve *fcu = (FCurve *)ale->key_data;
                bool intvals = (fcu->flag & FCURVE_INT_VALUES) != 0;
-               float unit_scale;
+               float unit_scale, offset;
                float cfra;
 
                /* F-Curve may not have any keyframes */
-               if (fcu->bezt == NULL)
+               if (fcu->bezt == NULL || (is_prop_edit && ale->tag == 0))
                        continue;
 
                /* convert current-frame to action-time (slightly less accurate, especially under
@@ -3912,78 +4433,147 @@ static void createTransGraphEditData(bContext *C, TransInfo *t)
                else
                        cfra = (float)CFRA;
 
-               unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, anim_map_flag);
+               unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, anim_map_flag, &offset);
+
+               /* only include BezTriples whose 'keyframe' occurs on the same side of the current frame as mouse (if applicable) */
+               for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
+                       if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
+                               const bool sel2 = (bezt->f2 & SELECT) != 0;
+                               const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2;
+                               const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2;
+
+                               TransDataCurveHandleFlags *hdata = NULL;
+                               /* short h1=1, h2=1; */ /* UNUSED */
+
+                               if (is_prop_edit) {
+                                       bool is_sel = (sel2 || sel1 || sel3);
+                                       /* we always select all handles for proportional editing if central handle is selected */
+                                       initTransDataCurveHandles(td, bezt);
+                                       bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 0, is_sel, true, intvals, mtx, smtx, unit_scale, offset);
+                                       initTransDataCurveHandles(td, bezt);
+                                       bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 1, is_sel, false, intvals, mtx, smtx, unit_scale, offset);
+                                       initTransDataCurveHandles(td, bezt);
+                                       bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 2, is_sel, true, intvals, mtx, smtx, unit_scale, offset);
+                               }
+                               else {
+                                       /* only include handles if selected, irrespective of the interpolation modes.
+                                        * also, only treat handles specially if the center point isn't selected.
+                                        */
+                                       if (!is_translation_mode || !(sel2)) {
+                                               if (sel1) {
+                                                       hdata = initTransDataCurveHandles(td, bezt);
+                                                       bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 0, sel1, true, intvals, mtx, smtx, unit_scale, offset);
+                                               }
+                                               else {
+                                                       /* h1 = 0; */ /* UNUSED */
+                                               }
+
+                                               if (sel3) {
+                                                       if (hdata == NULL)
+                                                               hdata = initTransDataCurveHandles(td, bezt);
+                                                       bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 2, sel3, true, intvals, mtx, smtx, unit_scale, offset);
+                                               }
+                                               else {
+                                                       /* h2 = 0; */ /* UNUSED */
+                                               }
+                                       }
+
+                                       /* only include main vert if selected */
+                                       if (sel2 && !use_local_center) {
+                                               /* move handles relative to center */
+                                               if (is_translation_mode) {
+                                                       if (sel1) td->flag |= TD_MOVEHANDLE1;
+                                                       if (sel3) td->flag |= TD_MOVEHANDLE2;
+                                               }
+
+                                               /* if handles were not selected, store their selection status */
+                                               if (!(sel1) || !(sel3)) {
+                                                       if (hdata == NULL)
+                                                               hdata = initTransDataCurveHandles(td, bezt);
+                                               }
+
+                                               bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 1, sel2, false, intvals, mtx, smtx, unit_scale, offset);
+
+                                       }
+                                       /* special hack (must be done after initTransDataCurveHandles(), as that stores handle settings to restore...):
+                                        *      - Check if we've got entire BezTriple selected and we're scaling/rotating that point,
+                                        *        then check if we're using auto-handles.
+                                        *      - If so, change them auto-handles to aligned handles so that handles get affected too
+                                        */
+                                       if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) &&
+                                           ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) &&
+                                           ELEM(t->mode, TFM_ROTATION, TFM_RESIZE))
+                                       {
+                                               if (hdata && (sel1) && (sel3)) {
+                                                       bezt->h1 = HD_ALIGN;
+                                                       bezt->h2 = HD_ALIGN;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               /* Sets handles based on the selection */
+               testhandles_fcurve(fcu, use_handle);
+       }
+
+       if (is_prop_edit) {
+               /* loop 2: build transdata arrays */
+               td = t->data;
+
+               for (ale = anim_data.first; ale; ale = ale->next) {
+                       AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
+                       FCurve *fcu = (FCurve *)ale->key_data;
+                       TransData *td_start = td;
+                       float cfra;
+
+                       /* F-Curve may not have any keyframes */
+                       if (fcu->bezt == NULL || (ale->tag == 0))
+                               continue;
+
+                       /* convert current-frame to action-time (slightly less accurate, especially under
+                        * higher scaling ratios, but is faster than converting all points)
+                        */
+                       if (adt)
+                               cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
+                       else
+                               cfra = (float)CFRA;
 
-               /* only include BezTriples whose 'keyframe' occurs on the same side of the current frame as mouse (if applicable) */
-               for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
-                       if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
-                               const bool sel2 = (bezt->f2 & SELECT) != 0;
-                               const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2;
-                               const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2;
+                       /* only include BezTriples whose 'keyframe' occurs on the same side of the current frame as mouse (if applicable) */
+                       for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
+                               if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) {
+                                       const bool sel2 = (bezt->f2 & SELECT) != 0;
+                                       const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2;
+                                       const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2;
 
-                               TransDataCurveHandleFlags *hdata = NULL;
-                               /* short h1=1, h2=1; */ /* UNUSED */
-                               
-                               /* only include handles if selected, irrespective of the interpolation modes.
-                                * also, only treat handles specially if the center point isn't selected. 
-                                */
-                               if (!is_translation_mode || !(sel2)) {
-                                       if (sel1) {
-                                               hdata = initTransDataCurveHandles(td, bezt);
-                                               bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 0, sel1, true, intvals, mtx, smtx, unit_scale);
+                                       if (sel1 || sel2) {
+                                               td->dist = td->rdist =  0.0f;
                                        }
                                        else {
-                                               /* h1 = 0; */ /* UNUSED */
+                                               graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
                                        }
-                                       
-                                       if (sel3) {
-                                               if (hdata == NULL)
-                                                       hdata = initTransDataCurveHandles(td, bezt);
-                                               bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 2, sel3, true, intvals, mtx, smtx, unit_scale);
+                                       td++;
+
+                                       if (sel2) {
+                                               td->dist = td->rdist = 0.0f;
                                        }
                                        else {
-                                               /* h2 = 0; */ /* UNUSED */
-                                       }
-                               }
-                               
-                               /* only include main vert if selected */
-                               if (sel2 && !use_local_center) {
-                                       /* move handles relative to center */
-                                       if (is_translation_mode) {
-                                               if (sel1) td->flag |= TD_MOVEHANDLE1;
-                                               if (sel3) td->flag |= TD_MOVEHANDLE2;
+                                               graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
                                        }
-                                       
-                                       /* if handles were not selected, store their selection status */
-                                       if (!(sel1) || !(sel3)) {
-                                               if (hdata == NULL)
-                                                       hdata = initTransDataCurveHandles(td, bezt);
+                                       td++;
+
+                                       if (sel3 || sel2) {
+                                               td->dist = td->rdist = 0.0f;
                                        }
-                                       
-                                       bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 1, sel2, false, intvals, mtx, smtx, unit_scale);
-                                       
-                               }
-                               /* special hack (must be done after initTransDataCurveHandles(), as that stores handle settings to restore...):
-                                *      - Check if we've got entire BezTriple selected and we're scaling/rotating that point,
-                                *        then check if we're using auto-handles.
-                                *      - If so, change them auto-handles to aligned handles so that handles get affected too
-                                */
-                               if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) &&
-                                   ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) &&
-                                   ELEM(t->mode, TFM_ROTATION, TFM_RESIZE))
-                               {
-                                       if (hdata && (sel1) && (sel3)) {
-                                               bezt->h1 = HD_ALIGN;
-                                               bezt->h2 = HD_ALIGN;
+                                       else {
+                                               graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle);
                                        }
+                                       td++;
                                }
                        }
                }
-               
-               /* Sets handles based on the selection */
-               testhandles_fcurve(fcu, use_handle);
        }
-       
+
        /* cleanup temp list */
        ANIM_animdata_freelist(&anim_data);
 }
@@ -4010,19 +4600,19 @@ static BeztMap *bezt_to_beztmaps(BezTriple *bezts, int totvert, const short UNUS
        BezTriple *prevbezt = NULL;
        BeztMap *bezm, *bezms;
        int i;
-       
+
        /* allocate memory for this array */
        if (totvert == 0 || bezts == NULL)
                return NULL;
        bezm = bezms = MEM_callocN(sizeof(BeztMap) * totvert, "BeztMaps");
-       
+
        /* assign beztriples to beztmaps */
        for (i = 0; i < totvert; i++, bezm++, prevbezt = bezt, bezt++) {
                bezm->bezt = bezt;
-               
+
                bezm->oldIndex = i;
                bezm->newIndex = i;
-               
+
                bezm->pipo = (prevbezt) ? prevbezt->ipo : bezt->ipo;
                bezm->cipo = bezt->ipo;
        }
@@ -4035,11 +4625,11 @@ static void sort_time_beztmaps(BeztMap *bezms, int totvert, const short UNUSED(u
 {
        BeztMap *bezm;
        int i, ok = 1;
-       
+
        /* keep repeating the process until nothing is out of place anymore */
        while (ok) {
                ok = 0;
-               
+
                bezm = bezms;
                i = totvert;
                while (i--) {
@@ -4048,13 +4638,13 @@ static void sort_time_beztmaps(BeztMap *bezms, int totvert, const short UNUSED(u
                                if (bezm->bezt->vec[1][0] > (bezm + 1)->bezt->vec[1][0]) {
                                        bezm->newIndex++;
                                        (bezm + 1)->newIndex--;
-                                       
+
                                        SWAP(BeztMap, *bezm, *(bezm + 1));
-                                       
+
                                        ok = 1;
                                }
                        }
-                       
+
                        /* do we need to check if the handles need to be swapped?
                         * optimization: this only needs to be performed in the first loop
                         */
@@ -4070,7 +4660,7 @@ static void sort_time_beztmaps(BeztMap *bezms, int totvert, const short UNUSED(u
                                        bezm->swapHs = -1;
                                }
                        }
-                       
+
                        bezm++;
                }
        }
@@ -4085,13 +4675,13 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totve
        TransData *td;
        int i, j;
        char *adjusted;
-       
+
        /* dynamically allocate an array of chars to mark whether an TransData's
         * pointers have been fixed already, so that we don't override ones that are
         * already done
         */
        adjusted = MEM_callocN(t->total, "beztmap_adjusted_map");
-       
+
        /* for each beztmap item, find if it is used anywhere */
        bezm = bezms;
        for (i = 0; i < totvert; i++, bezm++) {
@@ -4103,7 +4693,7 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totve
                for (j = 0; j < t->total; j++, td2d++, td++) {
                        /* skip item if already marked */
                        if (adjusted[j] != 0) continue;
-                       
+
                        /* update all transdata pointers, no need to check for selections etc,
                         * since only points that are really needed were created as transdata
                         */
@@ -4123,13 +4713,13 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totve
                        }
                        else if (td2d->loc2d == bezm->bezt->vec[1]) {
                                td2d->loc2d = (bezts + bezm->newIndex)->vec[1];
-                                       
+
                                /* if only control point is selected, the handle pointers need to be updated as well */
                                if (td2d->h1)
                                        td2d->h1 = (bezts + bezm->newIndex)->vec[0];
                                if (td2d->h2)
                                        td2d->h2 = (bezts + bezm->newIndex)->vec[2];
-                                       
+
                                adjusted[j] = 1;
                        }
 
@@ -4145,9 +4735,9 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totve
                                }
                        }
                }
-               
+
        }
-       
+
        /* free temp memory used for 'adjusted' array */
        MEM_freeN(adjusted);
 }
@@ -4163,27 +4753,27 @@ void remake_graph_transdata(TransInfo *t, ListBase *anim_data)
 {
        SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
        bAnimListElem *ale;
-       const short use_handle = !(sipo->flag & SIPO_NOHANDLES);
-       
+       const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
+
        /* sort and reassign verts */
        for (ale = anim_data->first; ale; ale = ale->next) {
                FCurve *fcu = (FCurve *)ale->key_data;
-               
+
                if (fcu->bezt) {
                        BeztMap *bezm;
-                       
+
                        /* adjust transform-data pointers */
                        /* note, none of these functions use 'use_handle', it could be removed */
                        bezm = bezt_to_beztmaps(fcu->bezt, fcu->totvert, use_handle);
                        sort_time_beztmaps(bezm, fcu->totvert, use_handle);
                        beztmap_to_data(t, fcu, bezm, fcu->totvert, use_handle);
-                       
+
                        /* free mapping stuff */
                        MEM_freeN(bezm);
-                       
+
                        /* re-sort actual beztriples (perhaps this could be done using the beztmaps to save time?) */
                        sort_time_fcurve(fcu);
-                       
+
                        /* make sure handles are all set correctly */
                        testhandles_fcurve(fcu, use_handle);
                }
@@ -4204,7 +4794,7 @@ void flushTransGraphData(TransInfo *t)
        int a;
 
        /* flush to 2d vector from internally used 3d vector */
-       for (a = 0, td = t->data, td2d = t->data2d, tdg = t->customData;
+       for (a = 0, td = t->data, td2d = t->data2d, tdg = t->custom.type.data;
             a < t->total;
             a++, td++, td2d++, tdg++)
        {
@@ -4214,18 +4804,18 @@ void flushTransGraphData(TransInfo *t)
                /* handle snapping for time values
                 *      - we should still be in NLA-mapping timespace
                 *      - only apply to keyframes (but never to handles)
-                *  - don't do this when cancelling, or else these changes won't go away
+                *  - don't do this when canceling, or else these changes won't go away
                 */
                if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0) {
                        switch (sipo->autosnap) {
                                case SACTSNAP_FRAME: /* snap to nearest frame */
                                        td2d->loc[0] = floor((double)td2d->loc[0] + 0.5);
                                        break;
-                               
+
                                case SACTSNAP_SECOND: /* snap to nearest second */
                                        td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf;
                                        break;
-                               
+
                                case SACTSNAP_MARKER: /* snap to nearest marker */
                                        td2d->loc[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, td2d->loc[0]);
                                        break;
@@ -4237,24 +4827,24 @@ void flushTransGraphData(TransInfo *t)
                        td2d->loc2d[0] = BKE_nla_tweakedit_remap(adt, td2d->loc[0], NLATIME_CONVERT_UNMAP);
                else
                        td2d->loc2d[0] = td2d->loc[0];
-                       
+
                /* Time-stepping auto-snapping modes don't get applied for Graph Editor transforms,
                 * as these use the generic transform modes which don't account for this sort of thing.
                 * These ones aren't affected by NLA mapping, so we do this after the conversion...
                 *
                 * NOTE: We also have to apply to td->loc, as that's what the handle-adjustment step below looks
                 *       to, otherwise we get "swimming handles"
-                * NOTE: We don't do this when cancelling transforms, or else these changes don't go away
+                * NOTE: We don't do this when canceling transforms, or else these changes don't go away
                 */
-               if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0 && 
-                   ELEM(sipo->autosnap, SACTSNAP_STEP, SACTSNAP_TSTEP)) 
+               if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0 &&
+                   ELEM(sipo->autosnap, SACTSNAP_STEP, SACTSNAP_TSTEP))
                {
                        switch (sipo->autosnap) {
                                case SACTSNAP_STEP: /* frame step */
                                        td2d->loc2d[0] = floor((double)td2d->loc[0] + 0.5);
                                        td->loc[0]     = floor((double)td->loc[0] + 0.5);
                                        break;
-                               
+
                                case SACTSNAP_TSTEP: /* second step */
                                        /* XXX: the handle behaviour in this case is still not quite right... */
                                        td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf;
@@ -4262,18 +4852,18 @@ void flushTransGraphData(TransInfo *t)
                                        break;
                        }
                }
-               
+
                /* if int-values only, truncate to integers */
                if (td->flag & TD_INTVALUES)
-                       td2d->loc2d[1] = floorf(td2d->loc[1] + 0.5f);
+                       td2d->loc2d[1] = floorf(td2d->loc[1] * inv_unit_scale - tdg->offset + 0.5f);
                else
-                       td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale;
-               
+                       td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale - tdg->offset;
+
                if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) {
                        td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0];
                        td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
                }
-               
+
                if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) {
                        td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0];
                        td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
@@ -4567,7 +5157,7 @@ static void SeqTransDataBounds(TransInfo *t, ListBase *seqbase, TransSeq *ts)
 }
 
 
-static void freeSeqData(TransInfo *t)
+static void freeSeqData(TransInfo *t, TransCustomData *custom_data)
 {
        Editing *ed = BKE_sequencer_editing_get(t->scene, false);
 
@@ -4601,44 +5191,47 @@ static void freeSeqData(TransInfo *t)
                        {
                                int overlap = 0;
 
-                               seq_prev = NULL;
-                               for (a = 0; a < t->total; a++, td++) {
+                               for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) {
                                        seq = ((TransDataSeq *)td->extra)->seq;
                                        if ((seq != seq_prev) && (seq->depth == 0) && (seq->flag & SEQ_OVERLAP)) {
                                                overlap = 1;
                                                break;
                                        }
-                                       seq_prev = seq;
                                }
 
                                if (overlap) {
-                                       bool has_effect = false;
+                                       bool has_effect_root = false, has_effect_any = false;
                                        for (seq = seqbasep->first; seq; seq = seq->next)
                                                seq->tmp = NULL;
 
                                        td = t->data;
-                                       seq_prev = NULL;
-                                       for (a = 0; a < t->total; a++, td++) {
+                                       for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) {
                                                seq = ((TransDataSeq *)td->extra)->seq;
                                                if ((seq != seq_prev)) {
                                                        /* check effects strips, we cant change their time */
                                                        if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) {
-                                                               has_effect = true;
+                                                               has_effect_any = true;
+                                                               if (seq->depth == 0) {
+                                                                       has_effect_root = true;
+                                                               }
                                                        }
                                                        else {
-                                                               /* Tag seq with a non zero value, used by BKE_sequence_base_shuffle_time to identify the ones to shuffle */
-                                                               seq->tmp = (void *)1;
+                                                               /* Tag seq with a non zero value,
+                                                                * used by BKE_sequence_base_shuffle_time to identify the ones to shuffle */
+                                                               if (seq->depth == 0) {
+                                                                       seq->tmp = (void *)1;
+                                                               }
                                                        }
                                                }
+
                                        }
 
                                        if (t->flag & T_ALT_TRANSFORM) {
                                                int minframe = MAXFRAME;
                                                td = t->data;
-                                               seq_prev = NULL;
-                                               for (a = 0; a < t->total; a++, td++) {
+                                               for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) {
                                                        seq = ((TransDataSeq *)td->extra)->seq;
-                                                       if ((seq != seq_prev)) {
+                                                       if ((seq != seq_prev) && (seq->depth == 0)) {
                                                                minframe = min_ii(minframe, seq->startdisp);
                                                        }
                                                }
@@ -4670,11 +5263,10 @@ static void freeSeqData(TransInfo *t)
                                                BKE_sequence_base_shuffle_time(seqbasep, t->scene);
                                        }
 
-                                       if (has_effect) {
+                                       if (has_effect_any) {
                                                /* update effects strips based on strips just moved in time */
                                                td = t->data;
-                                               seq_prev = NULL;
-                                               for (a = 0; a < t->total; a++, td++) {
+                                               for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) {
                                                        seq = ((TransDataSeq *)td->extra)->seq;
                                                        if ((seq != seq_prev)) {
                                                                if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) {
@@ -4682,13 +5274,14 @@ static void freeSeqData(TransInfo *t)
                                                                }
                                                        }
                                                }
+                                       }
 
+                                       if (has_effect_root) {
                                                /* now if any effects _still_ overlap, we need to move them up */
                                                td = t->data;
-                                               seq_prev = NULL;
-                                               for (a = 0; a < t->total; a++, td++) {
+                                               for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) {
                                                        seq = ((TransDataSeq *)td->extra)->seq;
-                                                       if ((seq != seq_prev)) {
+                                                       if ((seq != seq_prev) && (seq->depth == 0)) {
                                                                if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) {
                                                                        if (BKE_sequence_test_overlap(seqbasep, seq)) {
                                                                                BKE_sequence_base_shuffle(seqbasep, seq, t->scene);
@@ -4728,11 +5321,11 @@ static void freeSeqData(TransInfo *t)
                }
        }
 
-       if ((t->customData != NULL) && (t->flag & T_FREE_CUSTOMDATA)) {
-               TransSeq *ts = t->customData;
+       if ((custom_data->data != NULL) && custom_data->use_free) {
+               TransSeq *ts = custom_data->data;
                MEM_freeN(ts->tdseq);
-               MEM_freeN(t->customData);
-               t->customData = NULL;
+               MEM_freeN(custom_data->data);
+               custom_data->data = NULL;
        }
        if (t->data) {
                MEM_freeN(t->data); // XXX postTrans usually does this
@@ -4760,9 +5353,9 @@ static void createTransSeqData(bContext *C, TransInfo *t)
                return;
        }
 
-       t->customFree = freeSeqData;
+       t->custom.type.free_cb = freeSeqData;
 
-       xmouse = (int)UI_view2d_region_to_view_x(v2d, t->imval[0]);
+       xmouse = (int)UI_view2d_region_to_view_x(v2d, t->mouse.imval[0]);
 
        /* which side of the current frame should be allowed */
        if (t->mode == TFM_TIME_EXTEND) {
@@ -4806,16 +5399,16 @@ static void createTransSeqData(bContext *C, TransInfo *t)
                return;
        }
 
-       t->customData = ts = MEM_mallocN(sizeof(TransSeq), "transseq");
+       t->custom.type.data = ts = MEM_callocN(sizeof(TransSeq), "transseq");
+       t->custom.type.use_free = true;
        td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransSeq TransData");
        td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransSeq TransData2D");
        ts->tdseq = tdsq = MEM_callocN(t->total * sizeof(TransDataSeq), "TransSeq TransDataSeq");
-       t->flag |= T_FREE_CUSTOMDATA;
 
        /* loop 2: build transdata array */
        SeqToTransData_Recursive(t, ed->seqbasep, td, td2d, tdsq);
        SeqTransDataBounds(t, ed->seqbasep, ts);
-       
+
        /* set the snap mode based on how close the mouse is at the end/start points */
        if (abs(xmouse - ts->max) > abs(xmouse - ts->min))
                ts->snap_left = true;
@@ -4853,19 +5446,19 @@ static bool constraints_list_needinv(TransInfo *t, ListBase *list)
                                {
                                        return true;
                                }
-                               
+
                                /* constraints that require this only under special conditions */
                                if (con->type == CONSTRAINT_TYPE_CHILDOF) {
                                        /* ChildOf constraint only works when using all location components, see T42256. */
                                        bChildOfConstraint *data = (bChildOfConstraint *)con->data;
-                                       
+
                                        if ((data->flag & CHILDOF_LOCX) && (data->flag & CHILDOF_LOCY) && (data->flag & CHILDOF_LOCZ))
                                                return true;
                                }
                                else if (con->type == CONSTRAINT_TYPE_ROTLIKE) {
                                        /* CopyRot constraint only does this when rotating, and offset is on */
                                        bRotateLikeConstraint *data = (bRotateLikeConstraint *)con->data;
-                                       
+
                                        if ((data->flag & ROTLIKE_OFFSET) && (t->mode == TFM_ROTATION))
                                                return true;
                                }
@@ -4873,7 +5466,7 @@ static bool constraints_list_needinv(TransInfo *t, ListBase *list)
                                        /* Transform constraint needs it for rotation at least (r.57309),
                                         * but doing so when translating may also mess things up [#36203]
                                         */
-                                       
+
                                        if (t->mode == TFM_ROTATION)
                                                return true;
                                        /* ??? (t->mode == TFM_SCALE) ? */
@@ -4915,7 +5508,8 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
                        }
                        /* update object's loc/rot to get current rigid body transform */
                        mat4_to_loc_rot_size(ob->loc, rot, scale, ob->obmat);
-                       BKE_object_mat3_to_rot(ob, rot, false);
+                       sub_v3_v3(ob->loc, ob->dloc);
+                       BKE_object_mat3_to_rot(ob, rot, false); /* drot is already corrected here */
                }
        }
 
@@ -4937,13 +5531,9 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
                skip_invert = true;
 
        if (skip_invert == false && constinv == false) {
-               if (constinv == false)
-                       ob->transflag |= OB_NO_CONSTRAINTS;  /* BKE_object_where_is_calc_time checks this */
-               
+               ob->transflag |= OB_NO_CONSTRAINTS;  /* BKE_object_where_is_calc_time checks this */
                BKE_object_where_is_calc(t->scene, ob);
-               
-               if (constinv == false)
-                       ob->transflag &= ~OB_NO_CONSTRAINTS;
+               ob->transflag &= ~OB_NO_CONSTRAINTS;
        }
        else
                BKE_object_where_is_calc(t->scene, ob);
@@ -4952,13 +5542,13 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
 
        td->loc = ob->loc;
        copy_v3_v3(td->iloc, td->loc);
-       
+
        if (ob->rotmode > 0) {
                td->ext->rot = ob->rot;
                td->ext->rotAxis = NULL;
                td->ext->rotAngle = NULL;
                td->ext->quat = NULL;
-               
+
                copy_v3_v3(td->ext->irot, ob->rot);
                copy_v3_v3(td->ext->drot, ob->drot);
        }
@@ -4967,7 +5557,7 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
                td->ext->rotAxis = ob->rotAxis;
                td->ext->rotAngle = &ob->rotAngle;
                td->ext->quat = NULL;
-               
+
                td->ext->irotAngle = ob->rotAngle;
                copy_v3_v3(td->ext->irotAxis, ob->rotAxis);
                // td->ext->drotAngle = ob->drotAngle;                  // XXX, not implemented
@@ -4978,7 +5568,7 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
                td->ext->rotAxis = NULL;
                td->ext->rotAngle = NULL;
                td->ext->quat = ob->quat;
-               
+
                copy_qt_qt(td->ext->iquat, ob->quat);
                copy_qt_qt(td->ext->dquat, ob->dquat);
        }
@@ -5067,7 +5657,9 @@ static void set_trans_object_base_flags(TransInfo *t)
 
                        if (parsel) {
                                /* rotation around local centers are allowed to propagate */
-                               if ((t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL) && t->around == V3D_LOCAL) {
+                               if ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
+                                   (t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL))
+                               {
                                        base->flag |= BA_TRANSFORM_CHILD;
                                }
                                else {
@@ -5085,10 +5677,9 @@ static void set_trans_object_base_flags(TransInfo *t)
        /* and we store them temporal in base (only used for transform code) */
        /* this because after doing updates, the object->recalc is cleared */
        for (base = scene->base.first; base; base = base->next) {
-               if (base->object->recalc & OB_RECALC_OB)
-                       base->flag |= BA_HAS_RECALC_OB;
-               if (base->object->recalc & OB_RECALC_DATA)
-                       base->flag |= BA_HAS_RECALC_DATA;
+               if (base->object->recalc & (OB_RECALC_OB | OB_RECALC_DATA)) {
+                       base->flag |= BA_SNAP_FIX_DEPS_FIASCO;
+               }
        }
 }
 
@@ -5103,7 +5694,7 @@ static bool mark_children(Object *ob)
                        return true;
                }
        }
-       
+
        return false;
 }
 
@@ -5115,12 +5706,14 @@ static int count_proportional_objects(TransInfo *t)
        Base *base;
 
        /* rotations around local centers are allowed to propagate, so we take all objects */
-       if (!((t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL) && t->around == V3D_LOCAL)) {
+       if (!((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
+             (t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL)))
+       {
                /* mark all parents */
                for (base = scene->base.first; base; base = base->next) {
                        if (TESTBASELIB_BGMODE(v3d, scene, base)) {
                                Object *parent = base->object->parent;
-       
+
                                /* flag all parents */
                                while (parent) {
                                        parent->flag |= BA_TRANSFORM_PARENT;
@@ -5139,7 +5732,7 @@ static int count_proportional_objects(TransInfo *t)
                        }
                }
        }
-       
+
        for (base = scene->base.first; base; base = base->next) {
                Object *ob = base->object;
 
@@ -5153,7 +5746,7 @@ static int count_proportional_objects(TransInfo *t)
                        total += 1;
                }
        }
-       
+
 
        /* all recalc flags get flushed to all layers, so a layer flip later on works fine */
        DAG_scene_relations_update(G.main, t->scene);
@@ -5162,10 +5755,9 @@ static int count_proportional_objects(TransInfo *t)
        /* and we store them temporal in base (only used for transform code) */
        /* this because after doing updates, the object->recalc is cleared */
        for (base = scene->base.first; base; base = base->next) {
-               if (base->object->recalc & OB_RECALC_OB)
-                       base->flag |= BA_HAS_RECALC_OB;
-               if (base->object->recalc & OB_RECALC_DATA)
-                       base->flag |= BA_HAS_RECALC_DATA;
+               if (base->object->recalc & (OB_RECALC_OB | OB_RECALC_DATA)) {
+                       base->flag |= BA_SNAP_FIX_DEPS_FIASCO;
+               }
        }
 
        return total;
@@ -5180,7 +5772,7 @@ static void clear_trans_object_base_flags(TransInfo *t)
                if (base->flag & BA_WAS_SEL)
                        base->flag |= SELECT;
 
-               base->flag &= ~(BA_WAS_SEL | BA_HAS_RECALC_OB | BA_HAS_RECALC_DATA | BA_TEMP_TAG | BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT);
+               base->flag &= ~(BA_WAS_SEL | BA_SNAP_FIX_DEPS_FIASCO | BA_TEMP_TAG | BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT);
        }
 }
 
@@ -5190,72 +5782,75 @@ static void clear_trans_object_base_flags(TransInfo *t)
 // NOTE: context may not always be available, so must check before using it as it's a luxury for a few cases
 void autokeyframe_ob_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob, int tmode)
 {
+       Main *bmain = CTX_data_main(C);
        ID *id = &ob->id;
        FCurve *fcu;
-       
+
        // TODO: this should probably be done per channel instead...
        if (autokeyframe_cfra_can_key(scene, id)) {
                ReportList *reports = CTX_wm_reports(C);
+               ToolSettings *ts = scene->toolsettings;
                KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
                ListBase dsources = {NULL, NULL};
                float cfra = (float)CFRA; // xxx this will do for now
                short flag = 0;
-               
+
                /* get flags used for inserting keyframes */
                flag = ANIM_get_keyframing_flags(scene, 1);
-               
+
                /* add datasource override for the object */
-               ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL); 
-               
+               ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL);
+
                if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
-                       /* only insert into active keyingset 
-                        * NOTE: we assume here that the active Keying Set does not need to have its iterator overridden spe
+                       /* only insert into active keyingset
+                        * NOTE: we assume here that the active Keying Set does not need to have its iterator overridden
                         */
                        ANIM_apply_keyingset(C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, cfra);
                }
                else if (IS_AUTOKEY_FLAG(scene, INSERTAVAIL)) {
                        AnimData *adt = ob->adt;
-                       
+
                        /* only key on available channels */
                        if (adt && adt->action) {
                                for (fcu = adt->action->curves.first; fcu; fcu = fcu->next) {
                                        fcu->flag &= ~FCURVE_SELECTED;
-                                       insert_keyframe(reports, id, adt->action,
+                                       insert_keyframe(bmain, reports, id, adt->action,
                                                        (fcu->grp ? fcu->grp->name : NULL),
-                                                       fcu->rna_path, fcu->array_index, cfra, flag);
+                                                       fcu->rna_path, fcu->array_index, cfra,
+                                                       ts->keyframe_type, flag);
                                }
                        }
                }
                else if (IS_AUTOKEY_FLAG(scene, INSERTNEEDED)) {
                        bool do_loc = false, do_rot = false, do_scale = false;
-                       
+
                        /* filter the conditions when this happens (assume that curarea->spacetype==SPACE_VIE3D) */
                        if (tmode == TFM_TRANSLATION) {
                                do_loc = true;
                        }
-                       else if (tmode == TFM_ROTATION) {
-                               if (v3d->around == V3D_ACTIVE) {
+                       else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) {
+                               if (v3d->around == V3D_AROUND_ACTIVE) {
                                        if (ob != OBACT)
                                                do_loc = true;
                                }
-                               else if (v3d->around == V3D_CURSOR)
+                               else if (v3d->around == V3D_AROUND_CURSOR)
                                        do_loc = true;
-                               
+
                                if ((v3d->flag & V3D_ALIGN) == 0)
                                        do_rot = true;
                        }
                        else if (tmode == TFM_RESIZE) {
-                               if (v3d->around == V3D_ACTIVE) {
+                               if (v3d->around == V3D_AROUND_ACTIVE) {
                                        if (ob != OBACT)
                                                do_loc = true;
                                }
-                               else if (v3d->around == V3D_CURSOR)
+                               else if (v3d->around == V3D_AROUND_CURSOR)
                                        do_loc = true;
-                               
+
                                if ((v3d->flag & V3D_ALIGN) == 0)
                                        do_scale = true;
                        }
-                       
+
                        /* insert keyframes for the affected sets of channels using the builtin KeyingSets found */
                        if (do_loc) {
                                KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID);
@@ -5275,23 +5870,23 @@ void autokeyframe_ob_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob,
                        KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
                        ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
                }
-               
+
                /* only calculate paths if there are paths to be recalculated,
                 * assuming that since we've autokeyed the transforms this is
                 * now safe to apply...
-                * 
+                *
                 * NOTE: only do this when there's context info
                 */
                if (C && (ob->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) {
                        //ED_objects_clear_paths(C); // XXX for now, don't need to clear
                        ED_objects_recalculate_paths(C, scene);
-                       
-                       /* XXX: there's potential here for problems with unkeyed rotations/scale, 
+
+                       /* XXX: there's potential here for problems with unkeyed rotations/scale,
                         *      but for now (until proper data-locality for baking operations),
                         *      this should be a better fix for T24451 and T37755
                         */
                }
-               
+
                /* free temp info */
                BLI_freelistN(&dsources);
        }
@@ -5304,40 +5899,42 @@ void autokeyframe_ob_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob,
 // NOTE: context may not always be available, so must check before using it as it's a luxury for a few cases
 void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob, int tmode, short targetless_ik)
 {
+       Main *bmain = CTX_data_main(C);
        ID *id = &ob->id;
        AnimData *adt = ob->adt;
        bAction *act = (adt) ? adt->action : NULL;
        bPose   *pose = ob->pose;
        bPoseChannel *pchan;
        FCurve *fcu;
-       
+
        // TODO: this should probably be done per channel instead...
        if (autokeyframe_cfra_can_key(scene, id)) {
                ReportList *reports = CTX_wm_reports(C);
+               ToolSettings *ts = scene->toolsettings;
                KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene);
                float cfra = (float)CFRA;
                short flag = 0;
-               
+
                /* flag is initialized from UserPref keyframing settings
                 *      - special exception for targetless IK - INSERTKEY_MATRIX keyframes should get
                 *    visual keyframes even if flag not set, as it's not that useful otherwise
                 *        (for quick animation recording)
                 */
                flag = ANIM_get_keyframing_flags(scene, 1);
-               
-               if (targetless_ik) 
+
+               if (targetless_ik)
                        flag |= INSERTKEY_MATRIX;
-               
+
                for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
                        if (pchan->bone->flag & BONE_TRANSFORM) {
                                ListBase dsources = {NULL, NULL};
-                               
+
                                /* clear any 'unkeyed' flag it may have */
                                pchan->bone->flag &= ~BONE_UNKEYED;
-                               
+
                                /* add datasource override for the camera object */
-                               ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan); 
-                               
+                               ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan);
+
                                /* only insert into active keyingset? */
                                if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
                                        /* run the active Keying Set on the current datasource */
@@ -5350,13 +5947,17 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o
                                                        /* only insert keyframes for this F-Curve if it affects the current bone */
                                                        if (strstr(fcu->rna_path, "bones")) {
                                                                char *pchanName = BLI_str_quoted_substrN(fcu->rna_path, "bones[");
-                                                               
-                                                               /* only if bone name matches too... 
+
+                                                               /* only if bone name matches too...
                                                                 * NOTE: this will do constraints too, but those are ok to do here too?
                                                                 */
-                                                               if (pchanName && STREQ(pchanName, pchan->name)) 
-                                                                       insert_keyframe(reports, id, act, ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, fcu->array_index, cfra, flag);
-                                                                       
+                                                               if (pchanName && STREQ(pchanName, pchan->name)) {
+                                                                       insert_keyframe(bmain, reports, id, act,
+                                                                                       ((fcu->grp) ? (fcu->grp->name) : (NULL)),
+                                                                                       fcu->rna_path, fcu->array_index, cfra,
+                                                                                       ts->keyframe_type, flag);
+                                                               }
+
                                                                if (pchanName) MEM_freeN(pchanName);
                                                        }
                                                }
@@ -5365,7 +5966,7 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o
                                /* only insert keyframe if needed? */
                                else if (IS_AUTOKEY_FLAG(scene, INSERTNEEDED)) {
                                        bool do_loc = false, do_rot = false, do_scale = false;
-                                       
+
                                        /* filter the conditions when this happens (assume that curarea->spacetype==SPACE_VIE3D) */
                                        if (tmode == TFM_TRANSLATION) {
                                                if (targetless_ik)
@@ -5373,21 +5974,21 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o
                                                else
                                                        do_loc = true;
                                        }
-                                       else if (tmode == TFM_ROTATION) {
-                                               if (ELEM(v3d->around, V3D_CURSOR, V3D_ACTIVE))
+                                       else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) {
+                                               if (ELEM(v3d->around, V3D_AROUND_CURSOR, V3D_AROUND_ACTIVE))
                                                        do_loc = true;
-                                                       
+
                                                if ((v3d->flag & V3D_ALIGN) == 0)
                                                        do_rot = true;
                                        }
                                        else if (tmode == TFM_RESIZE) {
-                                               if (ELEM(v3d->around, V3D_CURSOR, V3D_ACTIVE))
+                                               if (ELEM(v3d->around, V3D_AROUND_CURSOR, V3D_AROUND_ACTIVE))
                                                        do_loc = true;
-                                                       
+
                                                if ((v3d->flag & V3D_ALIGN) == 0)
                                                        do_scale = true;
                                        }
-                                       
+
                                        if (do_loc) {
                                                KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID);
                                                ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
@@ -5406,13 +6007,13 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o
                                        KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOC_ROT_SCALE_ID);
                                        ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, cfra);
                                }
-                               
+
                                /* free temp info */
                                BLI_freelistN(&dsources);
                        }
                }
-               
-               /* do the bone paths 
+
+               /* do the bone paths
                 *  - only do this when there is context info, since we need that to resolve
                 *        how to do the updates and so on...
                 *      - do not calculate unless there are paths already to update...
@@ -5437,27 +6038,23 @@ static void special_aftertrans_update__movieclip(bContext *C, TransInfo *t)
 {
        SpaceClip *sc = t->sa->spacedata.first;
        MovieClip *clip = ED_space_clip_get_clip(sc);
-       MovieTrackingPlaneTrack *plane_track;
        ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(&clip->tracking);
-       int framenr = ED_space_clip_get_clip_frame_number(sc);
-
-       for (plane_track = plane_tracks_base->first;
+       const int framenr = ED_space_clip_get_clip_frame_number(sc);
+       /* Update coordinates of modified plane tracks. */
+       for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first;
             plane_track;
             plane_track = plane_track->next)
        {
                bool do_update = false;
-
                if (plane_track->flag & PLANE_TRACK_HIDDEN) {
                        continue;
                }
-
                do_update |= PLANE_TRACK_VIEW_SELECTED(plane_track) != 0;
                if (do_update == false) {
                        if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) {
                                int i;
                                for (i = 0; i < plane_track->point_tracksnr; i++) {
                                        MovieTrackingTrack *track = plane_track->point_tracks[i];
-
                                        if (TRACK_VIEW_SELECTED(sc, track)) {
                                                do_update = true;
                                                break;
@@ -5465,15 +6062,14 @@ static void special_aftertrans_update__movieclip(bContext *C, TransInfo *t)
                                }
                        }
                }
-
                if (do_update) {
                        BKE_tracking_track_plane_from_existing_motion(plane_track, framenr);
                }
        }
-
-       if (t->scene->nodetree) {
-               /* tracks can be used for stabilization nodes,
-                * flush update for such nodes */
+       if (t->scene->nodetree != NULL) {
+               /* Tracks can be used for stabilization nodes,
+                * flush update for such nodes.
+                */
                nodeUpdateID(t->scene->nodetree, &clip->id);
                WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL);
        }
@@ -5515,7 +6111,7 @@ static void special_aftertrans_update__mask(bContext *C, TransInfo *t)
 static void special_aftertrans_update__node(bContext *UNUSED(C), TransInfo *t)
 {
        const bool canceled = (t->state == TRANS_CANCEL);
-       
+
        if (canceled && t->remove_on_cancel) {
                /* remove selected nodes on cancel */
                SpaceNode *snode = (SpaceNode *)t->sa->spacedata.first;
@@ -5540,6 +6136,7 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
                BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
                BMesh *bm = em->bm;
                char hflag;
+               bool has_face_sel = (bm->totfacesel != 0);
 
                if (t->flag & T_MIRROR) {
                        TransData *td;
@@ -5562,11 +6159,17 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
                }
 
                EDBM_automerge(t->scene, t->obedit, true, hflag);
+
+               /* Special case, this is needed or faces won't re-select.
+                * Flush selected edges to faces. */
+               if (has_face_sel && (em->selectmode == SCE_SELECT_FACE)) {
+                       EDBM_selectmode_flush_ex(em, SCE_SELECT_EDGE);
+               }
        }
 }
 
 /* inserting keys, pointcache, redraw events... */
-/* 
+/*
  * note: sequencer freeing has its own function now because of a conflict with transform's order of freeing (campbell)
  *       Order changed, the sequencer stuff should go back in here
  * */
@@ -5576,17 +6179,20 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
 //     short redrawipo=0, resetslowpar=1;
        const bool canceled = (t->state == TRANS_CANCEL);
        const bool duplicate = (t->mode == TFM_TIME_DUPLICATE);
-       
+
        /* early out when nothing happened */
        if (t->total == 0 || t->mode == TFM_DUMMY)
                return;
-       
+
        if (t->spacetype == SPACE_VIEW3D) {
                if (t->obedit) {
+                       /* Special Exception:
+                        * We don't normally access 't->custom.mode' here, but its needed in this case. */
+
                        if (canceled == 0) {
                                /* we need to delete the temporary faces before automerging */
                                if (t->mode == TFM_EDGE_SLIDE) {
-                                       EdgeSlideData *sld = t->customData;
+                                       EdgeSlideData *sld = t->custom.mode.data;
 
                                        /* handle multires re-projection, done
                                         * on transform completion since it's
@@ -5599,7 +6205,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                                }
                                else if (t->mode == TFM_VERT_SLIDE) {
                                        /* as above */
-                                       VertSlideData *sld = t->customData;
+                                       VertSlideData *sld = t->custom.mode.data;
                                        projectVertSlideData(t, true);
                                        freeVertSlideTempFaces(sld);
                                }
@@ -5610,11 +6216,17 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                        }
                        else {
                                if (t->mode == TFM_EDGE_SLIDE) {
-                                       EdgeSlideData *sld = t->customData;
+                                       EdgeSlideData *sld = t->custom.mode.data;
 
                                        sld->perc = 0.0;
                                        projectEdgeSlideData(t, false);
                                }
+                               else if (t->mode == TFM_VERT_SLIDE) {
+                                       VertSlideData *sld = t->custom.mode.data;
+
+                                       sld->perc = 0.0;
+                                       projectVertSlideData(t, false);
+                               }
                        }
                }
        }
@@ -5654,10 +6266,10 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                special_aftertrans_update__node(C, t);
                if (canceled == 0) {
                        ED_node_post_apply_transform(C, snode->edittree);
-                       
+
                        ED_node_link_insert(t->sa);
                }
-               
+
                /* clear link line */
                ED_node_link_intersect_test(t->sa, 0);
        }
@@ -5672,26 +6284,26 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
        else if (t->spacetype == SPACE_ACTION) {
                SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first;
                bAnimContext ac;
-               
+
                /* initialize relevant anim-context 'context' data */
                if (ANIM_animdata_get_context(C, &ac) == 0)
                        return;
-                       
+
                ob = ac.obact;
-               
+
                if (ELEM(ac.datatype, ANIMCONT_DOPESHEET, ANIMCONT_SHAPEKEY)) {
                        ListBase anim_data = {NULL, NULL};
                        bAnimListElem *ale;
                        short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/);
-                       
+
                        /* get channels to work on */
                        ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-                       
+
                        /* these should all be F-Curves */
                        for (ale = anim_data.first; ale; ale = ale->next) {
                                AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
                                FCurve *fcu = (FCurve *)ale->key_data;
-                               
+
                                /* 3 cases here for curve cleanups:
                                 * 1) NOTRANSKEYCULL on     -> cleanup of duplicates shouldn't be done
                                 * 2) canceled == 0        -> user confirmed the transform, so duplicates should be removed
@@ -5701,15 +6313,15 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                                    ((canceled == 0) || (duplicate)) )
                                {
                                        if (adt) {
-                                               ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 1);
+                                               ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0);
                                                posttrans_fcurve_clean(fcu, false); /* only use handles in graph editor */
-                                               ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 1);
+                                               ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0);
                                        }
                                        else
                                                posttrans_fcurve_clean(fcu, false);  /* only use handles in graph editor */
                                }
                        }
-                       
+
                        /* free temp memory */
                        ANIM_animdata_freelist(&anim_data);
                }
@@ -5722,7 +6334,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                                else
                                        DAG_id_tag_update(&ob->id, OB_RECALC_OB);
                        }
-                       
+
                        /* 3 cases here for curve cleanups:
                         * 1) NOTRANSKEYCULL on     -> cleanup of duplicates shouldn't be done
                         * 2) canceled == 0        -> user confirmed the transform, so duplicates should be removed
@@ -5745,7 +6357,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                            ((canceled == 0) || (duplicate)))
                        {
                                bGPdata *gpd;
-                               
+
                                // XXX: BAD! this get gpencil datablocks directly from main db...
                                // but that's how this currently works :/
                                for (gpd = G.main->gpencil.first; gpd; gpd = gpd->id.next) {
@@ -5774,9 +6386,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                                }
                        }
                }
-               
+
                /* marker transform, not especially nice but we may want to move markers
-                * at the same time as keyframes in the dope sheet. 
+                * at the same time as keyframes in the dope sheet.
                 */
                if ((saction->flag & SACTION_MARKERS_MOVE) && (canceled == 0)) {
                        if (t->mode == TFM_TIME_TRANSLATE) {
@@ -5795,35 +6407,35 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                                ED_markers_post_apply_transform(ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side);
                        }
                }
-               
+
                /* make sure all F-Curves are set correctly */
                if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                        ANIM_editkeyframes_refresh(&ac);
-               
+
                /* clear flag that was set for time-slide drawing */
                saction->flag &= ~SACTION_MOVING;
        }
        else if (t->spacetype == SPACE_IPO) {
                SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first;
                bAnimContext ac;
-               const short use_handle = !(sipo->flag & SIPO_NOHANDLES);
-               
+               const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0;
+
                /* initialize relevant anim-context 'context' data */
                if (ANIM_animdata_get_context(C, &ac) == 0)
                        return;
-               
+
                if (ac.datatype) {
                        ListBase anim_data = {NULL, NULL};
                        bAnimListElem *ale;
                        short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE);
-                       
+
                        /* get channels to work on */
                        ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-                       
+
                        for (ale = anim_data.first; ale; ale = ale->next) {
                                AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
                                FCurve *fcu = (FCurve *)ale->key_data;
-                               
+
                                /* 3 cases here for curve cleanups:
                                 * 1) NOTRANSKEYCULL on     -> cleanup of duplicates shouldn't be done
                                 * 2) canceled == 0        -> user confirmed the transform, so duplicates should be removed
@@ -5841,11 +6453,11 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                                                posttrans_fcurve_clean(fcu, use_handle);
                                }
                        }
-                       
+
                        /* free temp memory */
                        ANIM_animdata_freelist(&anim_data);
                }
-               
+
                /* Make sure all F-Curves are set correctly, but not if transform was
                 * canceled, since then curves were already restored to initial state.
                 * Note: if the refresh is really needed after cancel then some way
@@ -5856,32 +6468,32 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
        }
        else if (t->spacetype == SPACE_NLA) {
                bAnimContext ac;
-               
+
                /* initialize relevant anim-context 'context' data */
                if (ANIM_animdata_get_context(C, &ac) == 0)
                        return;
-                       
+
                if (ac.datatype) {
                        ListBase anim_data = {NULL, NULL};
                        bAnimListElem *ale;
                        short filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT);
-                       
+
                        /* get channels to work on */
                        ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-                       
+
                        for (ale = anim_data.first; ale; ale = ale->next) {
                                NlaTrack *nlt = (NlaTrack *)ale->data;
-                               
+
                                /* make sure strips are in order again */
                                BKE_nlatrack_sort_strips(nlt);
-                               
+
                                /* remove the temp metas */
                                BKE_nlastrips_clear_metas(&nlt->strips, 0, 1);
                        }
-                       
+
                        /* free temp memory */
                        ANIM_animdata_freelist(&anim_data);
-                       
+
                        /* perform after-transfrom validation */
                        ED_nla_postop_refresh(&ac);
                }
@@ -5890,7 +6502,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                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, 'e');
+                       ED_mesh_mirror_spatial_table(t->obedit, em, NULL, NULL, 'e');
                }
        }
        else if ((t->flag & T_POSE) && (t->poseobj)) {
@@ -5964,7 +6576,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
 
                        if (td->flag & TD_NOACTION)
                                break;
-                       
+
                        if (td->flag & TD_SKIP)
                                continue;
 
@@ -5990,7 +6602,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
                        if (!canceled) {
                                autokeyframe_ob_cb_func(C, t->scene, (View3D *)t->view, ob, t->mode);
                        }
-                       
+
                        /* restore rigid body transform */
                        if (ob->rigidbody_object && canceled) {
                                float ctime = BKE_scene_frame_get(t->scene);
@@ -6001,12 +6613,6 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
        }
 
        clear_trans_object_base_flags(t);
-
-
-#if 0 // TRANSFORM_FIX_ME
-       if (resetslowpar)
-               reset_slowparents();
-#endif
 }
 
 int special_transform_moving(TransInfo *t)
@@ -6033,20 +6639,20 @@ static void createTransObject(bContext *C, TransInfo *t)
 
        TransData *td = NULL;
        TransDataExtension *tx;
-       int propmode = t->flag & T_PROP_EDIT;
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
 
        set_trans_object_base_flags(t);
 
        /* count */
        t->total = CTX_DATA_COUNT(C, selected_objects);
-       
+
        if (!t->total) {
                /* clear here, main transform function escapes too */
                clear_trans_object_base_flags(t);
                return;
        }
-       
-       if (propmode) {
+
+       if (is_prop_edit) {
                t->total += count_proportional_objects(t);
        }
 
@@ -6056,30 +6662,30 @@ static void createTransObject(bContext *C, TransInfo *t)
        CTX_DATA_BEGIN(C, Base *, base, selected_bases)
        {
                Object *ob = base->object;
-               
+
                td->flag = TD_SELECTED;
                td->protectflag = ob->protectflag;
                td->ext = tx;
                td->ext->rotOrder = ob->rotmode;
-               
+
                if (base->flag & BA_TRANSFORM_CHILD) {
                        td->flag |= TD_NOCENTER;
                        td->flag |= TD_NO_LOC;
                }
-               
+
                /* select linked objects, but skip them later */
-               if (ob->id.lib != NULL) {
+               if (ID_IS_LINKED(ob)) {
                        td->flag |= TD_SKIP;
                }
-               
+
                ObjectToTransData(t, td, ob);
                td->val = NULL;
                td++;
                tx++;
        }
        CTX_DATA_END;
-       
-       if (propmode) {
+
+       if (is_prop_edit) {
                View3D *v3d = t->view;
                Base *base;
 
@@ -6093,7 +6699,7 @@ static void createTransObject(bContext *C, TransInfo *t)
                                td->protectflag = ob->protectflag;
                                td->ext = tx;
                                td->ext->rotOrder = ob->rotmode;
-                               
+
                                ObjectToTransData(t, td, ob);
                                td->val = NULL;
                                td++;
@@ -6107,7 +6713,7 @@ static void createTransObject(bContext *C, TransInfo *t)
 static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const float dpi_fac)
 {
        float locx, locy;
-       
+
        /* account for parents (nested nodes) */
        if (node->parent) {
                nodeToView(node->parent, node->locx, node->locy, &locx, &locy);
@@ -6116,7 +6722,7 @@ static void NodeToTransData(TransData *td, TransData2D *td2d, bNode *node, const
                locx = node->locx;
                locy = node->locy;
        }
-       
+
        /* use top-left corner as the transform origin for nodes */
        /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */
 #ifdef USE_NODE_CENTER
@@ -6233,22 +6839,22 @@ typedef struct TransDataTracking {
 
 static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTracking *tdt,
                                   MovieTrackingTrack *track, MovieTrackingMarker *marker,
-                                  int area, float loc[2], float rel[2], const float off[2], float aspx, float aspy)
+                                  int area, float loc[2], float rel[2], const float off[2], const float aspect[2])
 {
        int anchor = area == TRACK_AREA_POINT && off;
 
        tdt->mode = transDataTracking_ModeTracks;
 
        if (anchor) {
-               td2d->loc[0] = rel[0] * aspx; /* hold original location */
-               td2d->loc[1] = rel[1] * aspy;
+               td2d->loc[0] = rel[0] * aspect[0]; /* hold original location */
+               td2d->loc[1] = rel[1] * aspect[1];
 
                tdt->loc = loc;
                td2d->loc2d = loc; /* current location */
        }
        else {
-               td2d->loc[0] = loc[0] * aspx; /* hold original location */
-               td2d->loc[1] = loc[1] * aspy;
+               td2d->loc[0] = loc[0] * aspect[0]; /* hold original location */
+               td2d->loc[1] = loc[1] * aspect[1];
 
                td2d->loc2d = loc; /* current location */
        }
@@ -6263,8 +6869,8 @@ static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTra
 
        if (rel) {
                if (!anchor) {
-                       td2d->loc[0] += rel[0] * aspx;
-                       td2d->loc[1] += rel[1] * aspy;
+                       td2d->loc[0] += rel[0] * aspect[0];
+                       td2d->loc[1] += rel[1] * aspect[1];
                }
 
                copy_v2_v2(tdt->srelative, rel);
@@ -6279,8 +6885,8 @@ static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTra
 
        //copy_v3_v3(td->center, td->loc);
        td->flag |= TD_INDIVIDUAL_SCALE;
-       td->center[0] = marker->pos[0] * aspx;
-       td->center[1] = marker->pos[1] * aspy;
+       td->center[0] = marker->pos[0] * aspect[0];
+       td->center[1] = marker->pos[1] * aspect[1];
 
        memset(td->axismtx, 0, sizeof(td->axismtx));
        td->axismtx[2][2] = 1.0f;
@@ -6295,8 +6901,9 @@ static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTra
        unit_m3(td->smtx);
 }
 
-static void trackToTransData(const int framenr, TransData *td, TransData2D *td2d,
-                             TransDataTracking *tdt, MovieTrackingTrack *track, float aspx, float aspy)
+static void trackToTransData(
+        const int framenr, TransData *td, TransData2D *td2d,
+        TransDataTracking *tdt, MovieTrackingTrack *track, const float aspect[2])
 {
        MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr);
 
@@ -6304,11 +6911,11 @@ static void trackToTransData(const int framenr, TransData *td, TransData2D *td2d
        marker->flag &= ~(MARKER_DISABLED | MARKER_TRACKED);
 
        markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_POINT,
-                             track->offset, marker->pos, track->offset, aspx, aspy);
+                             track->offset, marker->pos, track->offset, aspect);
 
        if (track->flag & SELECT) {
                markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_POINT,
-                                     marker->pos, NULL, NULL, aspx, aspy);
+                                     marker->pos, NULL, NULL, aspect);
        }
 
        if (track->pat_flag & SELECT) {
@@ -6316,28 +6923,28 @@ static void trackToTransData(const int framenr, TransData *td, TransData2D *td2d
 
                for (a = 0; a < 4; a++) {
                        markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_PAT,
-                                             marker->pattern_corners[a], marker->pos, NULL, aspx, aspy);
+                                             marker->pattern_corners[a], marker->pos, NULL, aspect);
                }
        }
 
        if (track->search_flag & SELECT) {
                markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_SEARCH,
-                                     marker->search_min, marker->pos, NULL, aspx, aspy);
+                                     marker->search_min, marker->pos, NULL, aspect);
 
                markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_SEARCH,
-                                     marker->search_max, marker->pos, NULL, aspx, aspy);
+                                     marker->search_max, marker->pos, NULL, aspect);
        }
 }
 
 static void planeMarkerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTracking *tdt,
                                        MovieTrackingPlaneTrack *plane_track, float corner[2],
-                                       float aspx, float aspy)
+                                       const float aspect[2])
 {
        tdt->mode = transDataTracking_ModePlaneTracks;
        tdt->plane_track = plane_track;
 
-       td2d->loc[0] = corner[0] * aspx; /* hold original location */
-       td2d->loc[1] = corner[1] * aspy;
+       td2d->loc[0] = corner[0] * aspect[0]; /* hold original location */
+       td2d->loc[1] = corner[1] * aspect[1];
 
        td2d->loc2d = corner; /* current location */
        td2d->loc[2] = 0.0f;
@@ -6362,7 +6969,7 @@ static void planeMarkerToTransDataInit(TransData *td, TransData2D *td2d, TransDa
 
 static void planeTrackToTransData(const int framenr, TransData *td, TransData2D *td2d,
                                   TransDataTracking *tdt, MovieTrackingPlaneTrack *plane_track,
-                                  float aspx, float aspy)
+                                  const float aspect[2])
 {
        MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_ensure(plane_track, framenr);
        int i;
@@ -6371,20 +6978,19 @@ static void planeTrackToTransData(const int framenr, TransData *td, TransData2D
        plane_marker->flag &= ~PLANE_MARKER_TRACKED;
 
        for (i = 0; i < 4; i++) {
-               planeMarkerToTransDataInit(td++, td2d++, tdt++, plane_track, plane_marker->corners[i], aspx, aspy);
+               planeMarkerToTransDataInit(td++, td2d++, tdt++, plane_track, plane_marker->corners[i], aspect);
        }
 }
 
-static void transDataTrackingFree(TransInfo *t)
+static void transDataTrackingFree(TransInfo *UNUSED(t), TransCustomData *custom_data)
 {
-       TransDataTracking *tdt = t->customData;
-
-       if (tdt) {
+       if (custom_data->data) {
+               TransDataTracking *tdt = custom_data->data;
                if (tdt->smarkers)
                        MEM_freeN(tdt->smarkers);
 
                MEM_freeN(tdt);
-               t->customData = NULL;
+               custom_data->data = NULL;
        }
 }
 
@@ -6400,7 +7006,6 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t)
        MovieTrackingPlaneTrack *plane_track;
        TransDataTracking *tdt;
        int framenr = ED_space_clip_get_clip_frame_number(sc);
-       float aspx, aspy;
 
        /* count */
        t->total = 0;
@@ -6435,19 +7040,17 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t)
        if (t->total == 0)
                return;
 
-       ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy);
-
        td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransTracking TransData");
        td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransTracking TransData2D");
-       tdt = t->customData = MEM_callocN(t->total * sizeof(TransDataTracking), "TransTracking TransDataTracking");
+       tdt = t->custom.type.data = MEM_callocN(t->total * sizeof(TransDataTracking), "TransTracking TransDataTracking");
 
-       t->customFree = transDataTrackingFree;
+       t->custom.type.free_cb = transDataTrackingFree;
 
        /* create actual data */
        track = tracksbase->first;
        while (track) {
                if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) {
-                       trackToTransData(framenr, td, td2d, tdt, track, aspx, aspy);
+                       trackToTransData(framenr, td, td2d, tdt, track, t->aspect);
 
                        /* offset */
                        td++;
@@ -6481,7 +7084,7 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t)
             plane_track = plane_track->next)
        {
                if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
-                       planeTrackToTransData(framenr, td, td2d, tdt, plane_track, aspx, aspy);
+                       planeTrackToTransData(framenr, td, td2d, tdt, plane_track, t->aspect);
                        td += 4;
                        td2d += 4;
                        tdt += 4;
@@ -6576,9 +7179,8 @@ static void createTransTrackingCurvesData(bContext *C, TransInfo *t)
 
        td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransTracking TransData");
        td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransTracking TransData2D");
-       tdt = t->customData = MEM_callocN(t->total * sizeof(TransDataTracking), "TransTracking TransDataTracking");
-
-       t->customFree = transDataTrackingFree;
+       t->custom.type.data = tdt = MEM_callocN(t->total * sizeof(TransDataTracking), "TransTracking TransDataTracking");
+       t->custom.type.free_cb = transDataTrackingFree;
 
        /* create actual data */
        track = tracksbase->first;
@@ -6642,10 +7244,11 @@ static void cancelTransTracking(TransInfo *t)
 {
        SpaceClip *sc = t->sa->spacedata.first;
        int i, framenr = ED_space_clip_get_clip_frame_number(sc);
+       TransDataTracking *tdt_array = t->custom.type.data;
 
        i = 0;
        while (i < t->total) {
-               TransDataTracking *tdt = (TransDataTracking *) t->customData + i;
+               TransDataTracking *tdt = &tdt_array[i];
 
                if (tdt->mode == transDataTracking_ModeTracks) {
                        MovieTrackingTrack *track = tdt->track;
@@ -6693,20 +7296,16 @@ static void cancelTransTracking(TransInfo *t)
 
 void flushTransTracking(TransInfo *t)
 {
-       SpaceClip *sc = t->sa->spacedata.first;
        TransData *td;
        TransData2D *td2d;
        TransDataTracking *tdt;
        int a;
-       float aspx, aspy;
-
-       ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy);
 
        if (t->state == TRANS_CANCEL)
                cancelTransTracking(t);
 
        /* flush to 2d vector from internally used 3d vector */
-       for (a = 0, td = t->data, td2d = t->data2d, tdt = t->customData; a < t->total; a++, td2d++, td++, tdt++) {
+       for (a = 0, td = t->data, td2d = t->data2d, tdt = t->custom.type.data; a < t->total; a++, td2d++, td++, tdt++) {
                if (tdt->mode == transDataTracking_ModeTracks) {
                        float loc2d[2];
 
@@ -6714,8 +7313,8 @@ void flushTransTracking(TransInfo *t)
                                continue;
                        }
 
-                       loc2d[0] = td2d->loc[0] / aspx;
-                       loc2d[1] = td2d->loc[1] / aspy;
+                       loc2d[0] = td2d->loc[0] / t->aspect[0];
+                       loc2d[1] = td2d->loc[1] / t->aspect[1];
 
                        if (t->flag & T_ALT_TRANSFORM) {
                                if (t->mode == TFM_RESIZE) {
@@ -6757,8 +7356,8 @@ void flushTransTracking(TransInfo *t)
                        td2d->loc2d[tdt->coord] = tdt->prev_pos[tdt->coord] + td2d->loc[1] * tdt->scale;
                }
                else if (tdt->mode == transDataTracking_ModePlaneTracks) {
-                       td2d->loc2d[0] = td2d->loc[0] / aspx;
-                       td2d->loc2d[1] = td2d->loc[1] / aspy;
+                       td2d->loc2d[0] = td2d->loc[0] / t->aspect[0];
+                       td2d->loc2d[1] = td2d->loc[1] / t->aspect[1];
                }
        }
 }
@@ -6836,9 +7435,10 @@ static void MaskHandleToTransData(MaskSplinePoint *point, eMaskWhichHandle which
        }
 }
 
-static void MaskPointToTransData(Scene *scene, MaskSplinePoint *point,
-                                 TransData *td, TransData2D *td2d, TransDataMasking *tdm,
-                                 const int propmode, const float asp[2])
+static void MaskPointToTransData(
+        Scene *scene, MaskSplinePoint *point,
+        TransData *td, TransData2D *td2d, TransDataMasking *tdm,
+        const bool is_prop_edit, const float asp[2])
 {
        BezTriple *bezt = &point->bezt;
        const bool is_sel_point = MASKPOINT_ISSEL_KNOT(point);
@@ -6848,7 +7448,7 @@ static void MaskPointToTransData(Scene *scene, MaskSplinePoint *point,
        BKE_mask_point_parent_matrix_get(point, CFRA, parent_matrix);
        invert_m3_m3(parent_inverse_matrix, parent_matrix);
 
-       if (propmode || is_sel_point) {
+       if (is_prop_edit || is_sel_point) {
                int i;
 
                tdm->point = point;
@@ -6968,7 +7568,7 @@ static void createTransMaskingData(bContext *C, TransInfo *t)
        TransData2D *td2d = NULL;
        TransDataMasking *tdm = NULL;
        int count = 0, countsel = 0;
-       int propmode = t->flag & T_PROP_EDIT;
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT);
        float asp[2];
 
        t->total = 0;
@@ -7018,7 +7618,7 @@ static void createTransMaskingData(bContext *C, TransInfo *t)
                                        }
                                }
 
-                               if (propmode)
+                               if (is_prop_edit)
                                        count += 3;
                        }
                }
@@ -7031,14 +7631,13 @@ static void createTransMaskingData(bContext *C, TransInfo *t)
 
        ED_mask_get_aspect(t->sa, t->ar, &asp[0], &asp[1]);
 
-       t->total = (propmode) ? count : countsel;
+       t->total = (is_prop_edit) ? count : countsel;
        td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Mask Editing)");
        /* for each 2d uv coord a 3d vector is allocated, so that they can be
         * treated just as if they were 3d verts */
        td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransObData2D(Mask Editing)");
-       tdm = t->customData = MEM_callocN(t->total * sizeof(TransDataMasking), "TransDataMasking(Mask Editing)");
-
-       t->flag |= T_FREE_CUSTOMDATA;
+       t->custom.type.data = tdm = MEM_callocN(t->total * sizeof(TransDataMasking), "TransDataMasking(Mask Editing)");
+       t->custom.type.use_free = true;
 
        /* create data */
        for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
@@ -7054,10 +7653,10 @@ static void createTransMaskingData(bContext *C, TransInfo *t)
                        for (i = 0; i < spline->tot_point; i++) {
                                MaskSplinePoint *point = &spline->points[i];
 
-                               if (propmode || MASKPOINT_ISSEL_ANY(point)) {
-                                       MaskPointToTransData(scene, point, td, td2d, tdm, propmode, asp);
+                               if (is_prop_edit || MASKPOINT_ISSEL_ANY(point)) {
+                                       MaskPointToTransData(scene, point, td, td2d, tdm, is_prop_edit, asp);
 
-                                       if (propmode || MASKPOINT_ISSEL_KNOT(point)) {
+                                       if (is_prop_edit || MASKPOINT_ISSEL_KNOT(point)) {
                                                td += 3;
                                                td2d += 3;
                                                tdm += 3;
@@ -7100,7 +7699,7 @@ void flushTransMasking(TransInfo *t)
        inv[1] = 1.0f / asp[1];
 
        /* flush to 2d vector from internally used 3d vector */
-       for (a = 0, td = t->data2d, tdm = t->customData; a < t->total; a++, td++, tdm++) {
+       for (a = 0, td = t->data2d, tdm = t->custom.type.data; a < t->total; a++, td++, tdm++) {
                td->loc2d[0] = td->loc[0] * inv[0];
                td->loc2d[1] = td->loc[1] * inv[1];
                mul_m3_v2(tdm->parent_inverse_matrix, td->loc2d);
@@ -7248,12 +7847,12 @@ static void createTransPaintCurveVerts(bContext *C, TransInfo *t)
        t->total = total;
        td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransData2D");
        td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransData");
-       tdpc = t->customData = MEM_callocN(t->total * sizeof(TransDataPaintCurve), "TransDataPaintCurve");
-       t->flag |= T_FREE_CUSTOMDATA;
+       t->custom.type.data = tdpc = MEM_callocN(t->total * sizeof(TransDataPaintCurve), "TransDataPaintCurve");
+       t->custom.type.use_free = true;
 
        for (pcp = pc->points, i = 0; i < pc->tot_points; i++, pcp++) {
                if (PC_IS_ANY_SEL(pcp)) {
-                       PaintCurvePointToTransData (pcp, td, td2d, tdpc);
+                       PaintCurvePointToTransData(pcp, td, td2d, tdpc);
 
                        if (pcp->bez.f2 & SELECT) {
                                td += 3;
@@ -7281,7 +7880,7 @@ void flushTransPaintCurve(TransInfo *t)
 {
        int i;
        TransData2D *td2d = t->data2d;
-       TransDataPaintCurve *tdpc = (TransDataPaintCurve *)t->customData;
+       TransDataPaintCurve *tdpc = t->custom.type.data;
 
        for (i = 0; i < t->total; i++, tdpc++, td2d++) {
                PaintCurvePoint *pcp = tdpc->pcp;
@@ -7296,14 +7895,14 @@ static void createTransGPencil(bContext *C, TransInfo *t)
        bGPDlayer *gpl;
        TransData *td = NULL;
        float mtx[3][3], smtx[3][3];
-       
+
        const Scene *scene = CTX_data_scene(C);
-       const int cfra = CFRA;
-       
-       const int propedit = (t->flag & T_PROP_EDIT);
-       const int propedit_connected = (t->flag & T_PROP_CONNECTED);
-       
-       
+       const int cfra_scene = CFRA;
+
+       const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
+       const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0;
+
+
        /* == Grease Pencil Strokes to Transform Data ==
         * Grease Pencil stroke points can be a mixture of 2D (screen-space),
         * or 3D coordinates. However, they're always saved as 3D points.
@@ -7311,30 +7910,32 @@ static void createTransGPencil(bContext *C, TransInfo *t)
         * strokes. This may cause issues in future though.
         */
        t->total = 0;
-       
+
        if (gpd == NULL)
                return;
-       
-       /* First Pass: Count the number of datapoints required for the strokes, 
+
+       /* First Pass: Count the number of datapoints required for the strokes,
         * (and add