Merged changes in the trunk up to revision 55357.
[blender-staging.git] / source / blender / editors / mesh / editmesh_select.c
index 17c6ebb8801e79769819a34de730e4bacb55032e..a1c302c6a63f102b1211673248e8772070cacd0f 100644 (file)
@@ -40,9 +40,9 @@
 
 #include "BKE_context.h"
 #include "BKE_displist.h"
-#include "BKE_depsgraph.h"
 #include "BKE_report.h"
 #include "BKE_paint.h"
+#include "BKE_mesh.h"
 #include "BKE_tessmesh.h"
 
 #include "IMB_imbuf_types.h"
@@ -57,7 +57,6 @@
 #include "ED_mesh.h"
 #include "ED_screen.h"
 #include "ED_uvedit.h"
-#include "ED_object.h"
 #include "ED_view3d.h"
 
 #include "BIF_gl.h"
 #include "DNA_scene_types.h"
 #include "DNA_object_types.h"
 #include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "GPU_extensions.h"
 
 #include "mesh_intern.h"
 
+#include "UI_resources.h"
 
 /* ****************************** MIRROR **************** */
 
@@ -105,21 +108,23 @@ void EDBM_select_mirrored(Object *UNUSED(obedit), BMEditMesh *em, int extend)
 
 void EDBM_automerge(Scene *scene, Object *obedit, int update)
 {
-       BMEditMesh *em;
        
        if ((scene->toolsettings->automerge) &&
            (obedit && obedit->type == OB_MESH))
        {
-               em = BMEdit_FromObject(obedit);
-               if (!em)
+               int ok;
+               BMEditMesh *em = BMEdit_FromObject(obedit);
+
+               if (!em) {
                        return;
+               }
+
+               ok = BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS,
+                                 "automerge verts=%hv dist=%f",
+                                 BM_ELEM_SELECT, scene->toolsettings->doublimit);
 
-               BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS,
-                            "automerge verts=%hv dist=%f",
-                            BM_ELEM_SELECT, scene->toolsettings->doublimit);
-               if (update) {
-                       DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
-                       BMEdit_RecalcTessellation(em);
+               if (LIKELY(ok) && update) {
+                       EDBM_update_generic(em, TRUE, TRUE);
                }
        }
 }
@@ -250,6 +255,9 @@ int EDBM_backbuf_border_mask_init(ViewContext *vc, const int mcords[][2], short
 
        dr = buf->rect;
 
+       if (vc->rv3d->gpuoffscreen)
+               GPU_offscreen_bind(vc->rv3d->gpuoffscreen);
+       
        /* draw the mask */
        glDisable(GL_DEPTH_TEST);
        
@@ -267,6 +275,9 @@ int EDBM_backbuf_border_mask_init(ViewContext *vc, const int mcords[][2], short
        
        glFinish(); /* to be sure readpixels sees mask */
        
+       if (vc->rv3d->gpuoffscreen)
+               GPU_offscreen_unbind(vc->rv3d->gpuoffscreen);
+       
        /* grab mask */
        bufmask = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
 
@@ -305,8 +316,10 @@ int EDBM_backbuf_circle_init(ViewContext *vc, short xs, short ys, short rads)
                        return 0;
                }
        }
-       else if (vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) return 0;
-       
+       else if (vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) {
+               return 0;
+       }
+
        xmin = xs - rads; xmax = xs + rads;
        ymin = ys - rads; ymax = ys + rads;
        buf = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax);
@@ -448,20 +461,6 @@ BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *r_dist, const short sel,
        }
 }
 
-/* returns labda for closest distance v1 to line-piece v2 - v3 */
-float labda_PdistVL2Dfl(const float v1[2], const float v2[2], const float v3[2])
-{
-       float rc[2], len;
-       
-       rc[0] = v3[0] - v2[0];
-       rc[1] = v3[1] - v2[1];
-       len = rc[0] * rc[0] + rc[1] * rc[1];
-       if (len == 0.0f)
-               return 0.0f;
-       
-       return (rc[0] * (v1[0] - v2[0]) + rc[1] * (v1[1] - v2[1])) / len;
-}
-
 /* note; uses v3d, so needs active 3d window */
 static void findnearestedge__doClosest(void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int UNUSED(index))
 {
@@ -476,12 +475,12 @@ static void findnearestedge__doClosest(void *userData, BMEdge *eed, const float
 
        if (distance < data->dist) {
                if (data->vc.rv3d->rflag & RV3D_CLIPPING) {
-                       float labda = labda_PdistVL2Dfl(data->mval_fl, screen_co_a, screen_co_b);
+                       float lambda = line_point_factor_v2(data->mval_fl, screen_co_a, screen_co_b);
                        float vec[3];
 
-                       vec[0] = eed->v1->co[0] + labda * (eed->v2->co[0] - eed->v1->co[0]);
-                       vec[1] = eed->v1->co[1] + labda * (eed->v2->co[1] - eed->v1->co[1]);
-                       vec[2] = eed->v1->co[2] + labda * (eed->v2->co[2] - eed->v1->co[2]);
+                       vec[0] = eed->v1->co[0] + lambda * (eed->v2->co[0] - eed->v1->co[0]);
+                       vec[1] = eed->v1->co[1] + lambda * (eed->v2->co[1] - eed->v1->co[1]);
+                       vec[2] = eed->v1->co[2] + lambda * (eed->v2->co[2] - eed->v1->co[2]);
 
                        if (ED_view3d_clipping_test(data->vc.rv3d, vec, TRUE) == 0) {
                                data->dist = distance;
@@ -585,7 +584,7 @@ BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist)
 
                        data.mval_fl[0] = vc->mval[0];
                        data.mval_fl[1] = vc->mval[1];
-                       data.dist = 0x7FFF;     /* largest short */
+                       data.dist = FLT_MAX;
                        data.toFace = efa;
 
                        mesh_foreachScreenFace(vc, findnearestface__getDistance, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
@@ -693,6 +692,9 @@ static EnumPropertyItem prop_similar_types[] = {
        {SIMEDGE_BEVEL, "BEVEL", 0, "Bevel", ""},
        {SIMEDGE_SEAM, "SEAM", 0, "Seam", ""},
        {SIMEDGE_SHARP, "SHARP", 0, "Sharpness", ""},
+#ifdef WITH_FREESTYLE
+       {SIMEDGE_FREESTYLE, "FREESTYLE_EDGE", 0, "Freestyle Edge Marks", ""},
+#endif
 
        {SIMFACE_MATERIAL, "MATERIAL", 0, "Material", ""},
        {SIMFACE_IMAGE, "IMAGE", 0, "Image", ""},
@@ -701,6 +703,9 @@ static EnumPropertyItem prop_similar_types[] = {
        {SIMFACE_PERIMETER, "PERIMETER", 0, "Perimeter", ""},
        {SIMFACE_NORMAL, "NORMAL", 0, "Normal", ""},
        {SIMFACE_COPLANAR, "COPLANAR", 0, "Co-planar", ""},
+#ifdef WITH_FREESTYLE
+       {SIMFACE_FREESTYLE, "FREESTYLE_FACE", 0, "Freestyle Face Marks", ""},
+#endif
 
        {0, NULL, 0, NULL, NULL}
 };
@@ -730,14 +735,14 @@ static int similar_face_select_exec(bContext *C, wmOperator *op)
        EDBM_flag_disable_all(em, BM_ELEM_SELECT);
 
        /* select the output */
-       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "faceout", BM_ALL, BM_ELEM_SELECT, TRUE);
+       BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, TRUE);
 
        /* finish the operator */
        if (!EDBM_op_finish(em, &bmop, op, TRUE)) {
                return OPERATOR_CANCELLED;
        }
 
-       EDBM_update_generic(C, em, FALSE);
+       EDBM_update_generic(em, FALSE, FALSE);
 
        /* we succeeded */
        return OPERATOR_FINISHED;
@@ -771,7 +776,7 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op)
        EDBM_flag_disable_all(em, BM_ELEM_SELECT);
 
        /* select the output */
-       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "edgeout", BM_ALL, BM_ELEM_SELECT, TRUE);
+       BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, TRUE);
        EDBM_selectmode_flush(em);
 
        /* finish the operator */
@@ -779,7 +784,7 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op)
                return OPERATOR_CANCELLED;
        }
 
-       EDBM_update_generic(C, em, FALSE);
+       EDBM_update_generic(em, FALSE, FALSE);
 
        /* we succeeded */
        return OPERATOR_FINISHED;
@@ -815,7 +820,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
        EDBM_flag_disable_all(em, BM_ELEM_SELECT);
 
        /* select the output */
-       BMO_slot_buffer_hflag_enable(em->bm, &bmop, "vertout", BM_ALL, BM_ELEM_SELECT, TRUE);
+       BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, TRUE);
 
        /* finish the operator */
        if (!EDBM_op_finish(em, &bmop, op, TRUE)) {
@@ -824,7 +829,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
 
        EDBM_selectmode_flush(em);
 
-       EDBM_update_generic(C, em, FALSE);
+       EDBM_update_generic(em, FALSE, FALSE);
 
        /* we succeeded */
        return OPERATOR_FINISHED;
@@ -875,7 +880,11 @@ static EnumPropertyItem *select_similar_type_itemf(bContext *C, PointerRNA *UNUS
                        }
                }
                else if (em->selectmode & SCE_SELECT_FACE) {
+#ifdef WITH_FREESTYLE
+                       for (a = SIMFACE_MATERIAL; a <= SIMFACE_FREESTYLE; a++) {
+#else
                        for (a = SIMFACE_MATERIAL; a <= SIMFACE_COPLANAR; a++) {
+#endif
                                RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
                        }
                }
@@ -915,6 +924,78 @@ void MESH_OT_select_similar(wmOperatorType *ot)
        RNA_def_float(ot->srna, "threshold", 0.0, 0.0, 1.0, "Threshold", "", 0.0, 1.0);
 }
 
+
+/* ****************  Mode Select *************** */
+
+static int edbm_select_mode_exec(bContext *C, wmOperator *op)
+{
+       const int type       = RNA_enum_get(op->ptr,    "type");
+       const int action     = RNA_enum_get(op->ptr,    "action");
+       const int use_extend = RNA_boolean_get(op->ptr, "use_extend");
+       const int use_expand = RNA_boolean_get(op->ptr, "use_expand");
+
+       if (EDBM_selectmode_toggle(C, type, action, use_extend, use_expand)) {
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+}
+
+static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+       /* detecting these options based on shift/ctrl here is weak, but it's done
+        * to make this work when clicking buttons or menus */
+       if (!RNA_struct_property_is_set(op->ptr, "use_extend"))
+               RNA_boolean_set(op->ptr, "use_extend", event->shift);
+       if (!RNA_struct_property_is_set(op->ptr, "use_expand"))
+               RNA_boolean_set(op->ptr, "use_expand", event->ctrl);
+
+       return edbm_select_mode_exec(C, op);
+}
+
+void MESH_OT_select_mode(wmOperatorType *ot)
+{
+       PropertyRNA *prop;
+
+       static EnumPropertyItem elem_items[] = {
+               {SCE_SELECT_VERTEX, "VERT", ICON_VERTEXSEL, "Vertices", ""},
+               {SCE_SELECT_EDGE,   "EDGE", ICON_EDGESEL, "Edges", ""},
+               {SCE_SELECT_FACE,   "FACE", ICON_FACESEL, "Faces", ""},
+               {0, NULL, 0, NULL, NULL},
+       };
+
+       static EnumPropertyItem actions_items[] = {
+               {0, "DISABLE", 0, "Disable", "Disable selected markers"},
+               {1, "ENABLE", 0, "Enable", "Enable selected markers"},
+               {2, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
+               {0, NULL, 0, NULL, NULL}
+       };
+
+       /* identifiers */
+       ot->name = "Select Mode";
+       ot->idname = "MESH_OT_select_mode";
+       ot->description = "Change selection mode";
+
+       /* api callbacks */
+       ot->invoke = edbm_select_mode_invoke;
+       ot->exec = edbm_select_mode_exec;
+       ot->poll = ED_operator_editmesh;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* properties */
+       prop = RNA_def_boolean(ot->srna, "use_extend", FALSE, "Extend", "");
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+       prop = RNA_def_boolean(ot->srna, "use_expand", FALSE, "Expand", "");
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+       ot->prop = prop = RNA_def_enum(ot->srna, "type", elem_items, 0, "Type", "");
+       RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+       RNA_def_enum(ot->srna, "action", actions_items, 2, "Action", "Selection action to execute");
+}
+
 /* ***************************************************** */
 
 /* ****************  LOOP SELECTS *************** */
@@ -929,8 +1010,8 @@ static void walker_select(BMEditMesh *em, int walkercode, void *start, int selec
                 BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
                 BMW_FLAG_TEST_HIDDEN,
                 BMW_NIL_LAY);
-       ele = BMW_begin(&walker, start);
-       for (; ele; ele = BMW_step(&walker)) {
+
+       for (ele = BMW_begin(&walker, start); ele; ele = BMW_step(&walker)) {
                if (!select) {
                        BM_select_history_remove(bm, ele);
                }
@@ -1013,7 +1094,7 @@ void MESH_OT_loop_multi_select(wmOperatorType *ot)
 
 /* ***************** loop select (non modal) ************** */
 
-static void mouse_mesh_loop(bContext *C, int mval[2], short extend, short ring)
+static void mouse_mesh_loop(bContext *C, const int mval[2], short extend, short deselect, short toggle, short ring)
 {
        ViewContext vc;
        BMEditMesh *em;
@@ -1032,14 +1113,20 @@ static void mouse_mesh_loop(bContext *C, int mval[2], short extend, short ring)
 
        eed = EDBM_edge_find_nearest(&vc, &dist);
        if (eed) {
-               if (extend == 0) {
+               if (extend == 0 && deselect == 0 && toggle == 0) {
                        EDBM_flag_disable_all(em, BM_ELEM_SELECT);
                }
        
-               if (BM_elem_flag_test(eed, BM_ELEM_SELECT) == 0) {
+               if (extend) {
                        select = TRUE;
                }
-               else if (extend) {
+               else if (deselect) {
+                       select = FALSE;
+               }
+               else if (BM_elem_flag_test(eed, BM_ELEM_SELECT) == 0) {
+                       select = TRUE;
+               }
+               else if (toggle) {
                        select = FALSE;
                }
 
@@ -1074,11 +1161,11 @@ static void mouse_mesh_loop(bContext *C, int mval[2], short extend, short ring)
                                /* We can't be sure this has already been set... */
                                ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
 
-                               if (ED_view3d_project_float_object(vc.ar, eed->v1->co, v1_co, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
+                               if (ED_view3d_project_float_object(vc.ar, eed->v1->co, v1_co, V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
                                        length_1 = len_squared_v2v2(mvalf, v1_co);
                                }
 
-                               if (ED_view3d_project_float_object(vc.ar, eed->v2->co, v2_co, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
+                               if (ED_view3d_project_float_object(vc.ar, eed->v2->co, v2_co, V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
                                        length_2 = len_squared_v2v2(mvalf, v2_co);
                                }
 #if 0
@@ -1094,7 +1181,7 @@ static void mouse_mesh_loop(bContext *C, int mval[2], short extend, short ring)
                                /* Select the face of eed which is the nearest of mouse. */
                                BMFace *f, *efa = NULL;
                                BMIter iterf;
-                               float best_dist = MAXFLOAT;
+                               float best_dist = FLT_MAX;
 
                                /* We can't be sure this has already been set... */
                                ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
@@ -1105,7 +1192,7 @@ static void mouse_mesh_loop(bContext *C, int mval[2], short extend, short ring)
                                                float co[2], tdist;
 
                                                BM_face_calc_center_mean(f, cent);
-                                               if (ED_view3d_project_float_object(vc.ar, cent, co, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
+                                               if (ED_view3d_project_float_object(vc.ar, cent, co, V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
                                                        tdist = len_squared_v2v2(mvalf, co);
                                                        if (tdist < best_dist) {
 /*                                                             printf("Best face: %p (%f)\n", f, tdist);*/
@@ -1126,12 +1213,14 @@ static void mouse_mesh_loop(bContext *C, int mval[2], short extend, short ring)
        }
 }
 
-static int edbm_select_loop_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int edbm_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
        
        view3d_operator_needs_opengl(C);
        
        mouse_mesh_loop(C, event->mval, RNA_boolean_get(op->ptr, "extend"),
+                       RNA_boolean_get(op->ptr, "deselect"),
+                       RNA_boolean_get(op->ptr, "toggle"),
                        RNA_boolean_get(op->ptr, "ring"));
        
        /* cannot do tweaks for as long this keymap is after transform map */
@@ -1154,6 +1243,8 @@ void MESH_OT_loop_select(wmOperatorType *ot)
        
        /* properties */
        RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection");
+       RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection");
+       RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection");
        RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring");
 }
 
@@ -1172,47 +1263,63 @@ void MESH_OT_edgering_select(wmOperatorType *ot)
        ot->flag = OPTYPE_UNDO;
 
        RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
+       RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection");
+       RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection");
        RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring");
 }
 
-/* ******************* edgetag_shortest_path and helpers ****************** */
+/* ******************* generic tag_shortest_path and helpers ****************** */
 
-static float edgetag_cut_cost(BMEdge *e1, BMEdge *e2, BMVert *v)
+static float step_cost_3_v3(const float v1[3], const float v2[3], const float v3[3])
 {
-       BMVert *v1 = (e1->v1 == v) ? e1->v2 : e1->v1;
-       BMVert *v2 = (e2->v1 == v) ? e2->v2 : e2->v1;
        float cost, d1[3], d2[3];
 
+
        /* The cost is based on the simple sum of the length of the two edgees... */
-       sub_v3_v3v3(d1, v->co, v1->co);
-       sub_v3_v3v3(d2, v2->co, v->co);
-       cost = len_v3(d1) + len_v3(d2);
+       sub_v3_v3v3(d1, v2, v1);
+       sub_v3_v3v3(d2, v3, v2);
+       cost = normalize_v3(d1) + normalize_v3(d2);
 
        /* but is biased to give higher values to sharp turns, so that it will take
         * paths with fewer "turns" when selecting between equal-weighted paths between
         * the two edges */
-       cost = cost + 0.5f * cost * (2.0f - sqrtf(fabsf(dot_v3v3(d1, d2))));
+       cost = cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v3v3(d1, d2)))));
 
        return cost;
 }
 
-static void edgetag_add_adjacent(Heap *heap, BMEdge *e1, BMVert *v, BMEdge **edges_prev, float *cost)
+/* ******************* edgetag_shortest_path and helpers ****************** */
+
+static float edgetag_cut_cost(BMEdge *e1, BMEdge *e2, BMVert *v)
+{
+       BMVert *v1 = BM_edge_other_vert(e1, v);
+       BMVert *v2 = BM_edge_other_vert(e2, v);
+       return step_cost_3_v3(v1->co, v->co, v2->co);
+}
+
+static void edgetag_add_adjacent(Heap *heap, BMEdge *e1, BMEdge **edges_prev, float *cost)
 {
+       BMIter viter;
+       BMVert *v;
+
        BMIter eiter;
        BMEdge *e2;
 
        const int e1_index = BM_elem_index_get(e1);
 
-       BM_ITER_ELEM (e2, &eiter, v, BM_EDGES_OF_VERT) {
-               if (!BM_elem_flag_test(e2, BM_ELEM_TAG)) {
-                       const int e2_index = BM_elem_index_get(e2);
-                       const float cost_cut = edgetag_cut_cost(e1, e2, v);
-                       const float cost_new = cost[e1_index] + cost_cut;
-
-                       if (cost[e2_index] > cost_new) {
-                               cost[e2_index] = cost_new;
-                               edges_prev[e2_index] = e1;
-                               BLI_heap_insert(heap, cost_new, e2);
+       BM_ITER_ELEM (v, &viter, e1, BM_VERTS_OF_EDGE) {
+               BM_ITER_ELEM (e2, &eiter, v, BM_EDGES_OF_VERT) {
+                       if (!BM_elem_flag_test(e2, BM_ELEM_TAG)) {
+                               /* we know 'e2' is not visited, check it out! */
+                               const int e2_index = BM_elem_index_get(e2);
+                               const float cost_cut = edgetag_cut_cost(e1, e2, v);
+                               const float cost_new = cost[e1_index] + cost_cut;
+
+                               if (cost[e2_index] > cost_new) {
+                                       cost[e2_index] = cost_new;
+                                       edges_prev[e2_index] = e1;
+                                       BLI_heap_insert(heap, cost_new, e2);
+                               }
                        }
                }
        }
@@ -1237,6 +1344,23 @@ static void edgetag_context_set(BMesh *bm, Scene *scene, BMEdge *e, int val)
                case EDGE_MODE_TAG_BEVEL:
                        BM_elem_float_data_set(&bm->edata, e, CD_BWEIGHT, (val) ? 1.0f : 0.0f);
                        break;
+#ifdef WITH_FREESTYLE
+               case EDGE_MODE_TAG_FREESTYLE:
+                       {
+                               FreestyleEdge *fed;
+
+                               if (!CustomData_has_layer(&bm->pdata, CD_FREESTYLE_FACE)) {
+                                       BM_data_layer_add(bm, &bm->pdata, CD_FREESTYLE_FACE);
+                               }
+
+                               fed = CustomData_bmesh_get(&bm->edata, e->head.data, CD_FREESTYLE_EDGE);
+                               if (!val)
+                                       fed->flag &= ~FREESTYLE_EDGE_MARK;
+                               else
+                                       fed->flag |= FREESTYLE_EDGE_MARK;
+                       }
+                       break;
+#endif
        }
 }
 
@@ -1253,10 +1377,34 @@ static int edgetag_context_check(Scene *scene, BMesh *bm, BMEdge *e)
                        return BM_elem_float_data_get(&bm->edata, e, CD_CREASE) ? TRUE : FALSE;
                case EDGE_MODE_TAG_BEVEL:
                        return BM_elem_float_data_get(&bm->edata, e, CD_BWEIGHT) ? TRUE : FALSE;
+#ifdef WITH_FREESTYLE
+               case EDGE_MODE_TAG_FREESTYLE:
+                       {
+                               FreestyleEdge *fed = CustomData_bmesh_get(&bm->edata, e->head.data, CD_FREESTYLE_EDGE);
+                               return (!fed) ? FALSE : (fed->flag & FREESTYLE_EDGE_MARK) ? TRUE : FALSE;
+                       }
+                       break;
+#endif
        }
        return 0;
 }
 
+static void edgetag_ensure_cd_flag(Scene *scene, Mesh *me)
+{
+       BMesh *bm = me->edit_btmesh->bm;
+
+       switch (scene->toolsettings->edge_mode) {
+               case EDGE_MODE_TAG_CREASE:
+                       BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE);
+                       break;
+               case EDGE_MODE_TAG_BEVEL:
+                       BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT);
+                       break;
+               default:
+                       break;
+       }
+}
+
 static int edgetag_shortest_path(Scene *scene, BMesh *bm, BMEdge *e_src, BMEdge *e_dst)
 {
        /* BM_ELEM_TAG flag is used to store visited edges */
@@ -1270,6 +1418,8 @@ static int edgetag_shortest_path(Scene *scene, BMesh *bm, BMEdge *e_src, BMEdge
        /* note, would pass BM_EDGE except we are looping over all edges anyway */
        BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */);
 
+       edgetag_ensure_cd_flag(scene, OBACT->data);
+
        BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) {
                if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == FALSE) {
                        BM_elem_flag_disable(e, BM_ELEM_TAG);
@@ -1315,20 +1465,19 @@ static int edgetag_shortest_path(Scene *scene, BMesh *bm, BMEdge *e_src, BMEdge
 
                if (!BM_elem_flag_test(e, BM_ELEM_TAG)) {
                        BM_elem_flag_enable(e, BM_ELEM_TAG);
-                       edgetag_add_adjacent(heap, e, e->v1, edges_prev, cost);
-                       edgetag_add_adjacent(heap, e, e->v2, edges_prev, cost);
+                       edgetag_add_adjacent(heap, e, edges_prev, cost);
                }
        }
        
        if (e == e_dst) {
-               short allseams = 1;
+               short all_set = TRUE;
 
                /* Check whether the path is already completely tagged.
                 * if it is, the tags will be cleared instead of set. */
                e = e_dst;
                do {
                        if (!edgetag_context_check(scene, bm, e)) {
-                               allseams = 0;
+                               all_set = FALSE;
                                break;
                        }
                } while ((e = edges_prev[BM_elem_index_get(e)]));
@@ -1336,7 +1485,7 @@ static int edgetag_shortest_path(Scene *scene, BMesh *bm, BMEdge *e_src, BMEdge
                /* Follow path back and source and add or remove tags */
                e = e_dst;
                do {
-                       edgetag_context_set(bm, scene, e, !allseams);
+                       edgetag_context_set(bm, scene, e, !all_set);
                } while ((e = edges_prev[BM_elem_index_get(e)]));
        }
 
@@ -1350,16 +1499,16 @@ static int edgetag_shortest_path(Scene *scene, BMesh *bm, BMEdge *e_src, BMEdge
 /* ******************* mesh shortest path select, uses prev-selected edge ****************** */
 
 /* since you want to create paths with multiple selects, it doesn't have extend option */
-static int mouse_mesh_shortest_path_edge(bContext *C, ViewContext *vc)
+static int mouse_mesh_shortest_path_edge(ViewContext *vc)
 {
        BMEditMesh *em = vc->em;
-       BMEdge *e;
+       BMEdge *e_dst;
        float dist = 75.0f;
        
-       e = EDBM_edge_find_nearest(vc, &dist);
-       if (e) {
+       e_dst = EDBM_edge_find_nearest(vc, &dist);
+       if (e_dst) {
                Mesh *me = vc->obedit->data;
-               int path = 0;
+               bool is_path = false;
                
                if (em->bm->selected.last) {
                        BMEditSelection *ese = em->bm->selected.last;
@@ -1367,29 +1516,30 @@ static int mouse_mesh_shortest_path_edge(bContext *C, ViewContext *vc)
                        if (ese && ese->htype == BM_EDGE) {
                                BMEdge *e_act;
                                e_act = (BMEdge *)ese->ele;
-                               if (e_act != e) {
-                                       if (edgetag_shortest_path(vc->scene, em->bm, e_act, e)) {
+                               if (e_act != e_dst) {
+                                       if (edgetag_shortest_path(vc->scene, em->bm, e_act, e_dst)) {
                                                BM_select_history_remove(em->bm, e_act);
-                                               path = 1;
+                                               is_path = true;
                                        }
                                }
                        }
                }
-               if (path == 0) {
-                       int act = (edgetag_context_check(vc->scene, em->bm, e) == 0);
-                       edgetag_context_set(em->bm, vc->scene, e, act); /* switch the edge option */
+               if (is_path == false) {
+                       int act = (edgetag_context_check(vc->scene, em->bm, e_dst) == 0);
+                       edgetag_ensure_cd_flag(vc->scene, vc->obedit->data);
+                       edgetag_context_set(em->bm, vc->scene, e_dst, act); /* switch the edge option */
                }
                
                EDBM_selectmode_flush(em);
 
                /* even if this is selected it may not be in the selection list */
-               if (edgetag_context_check(vc->scene, em->bm, e) == 0)
-                       BM_select_history_remove(em->bm, e);
+               if (edgetag_context_check(vc->scene, em->bm, e_dst) == 0)
+                       BM_select_history_remove(em->bm, e_dst);
                else
-                       BM_select_history_store(em->bm, e);
+                       BM_select_history_store(em->bm, e_dst);
        
                /* force drawmode for mesh */
-               switch (CTX_data_tool_settings(C)->edge_mode) {
+               switch (vc->scene->toolsettings->edge_mode) {
                        
                        case EDGE_MODE_TAG_SEAM:
                                me->drawflag |= ME_DRAWSEAMS;
@@ -1404,9 +1554,14 @@ static int mouse_mesh_shortest_path_edge(bContext *C, ViewContext *vc)
                        case EDGE_MODE_TAG_BEVEL:
                                me->drawflag |= ME_DRAWBWEIGHTS;
                                break;
+#ifdef WITH_FREESTYLE
+                       case EDGE_MODE_TAG_FREESTYLE:
+                               me->drawflag |= ME_DRAW_FREESTYLE_EDGE;
+                               break;
+#endif
                }
                
-               EDBM_update_generic(C, em, FALSE);
+               EDBM_update_generic(em, FALSE, FALSE);
 
                return TRUE;
        }
@@ -1421,8 +1576,6 @@ static int mouse_mesh_shortest_path_edge(bContext *C, ViewContext *vc)
 
 static float facetag_cut_cost(BMFace *f1, BMFace *f2, BMEdge *e)
 {
-       float cost, d1[3], d2[3];
-
        float f1_cent[3];
        float f2_cent[3];
        float e_cent[3];
@@ -1431,17 +1584,7 @@ static float facetag_cut_cost(BMFace *f1, BMFace *f2, BMEdge *e)
        BM_face_calc_center_mean(f2, f2_cent);
        mid_v3_v3v3(e_cent, e->v1->co, e->v2->co);
 
-       /* The cost is based on the simple sum of the length of the two edgees... */
-       sub_v3_v3v3(d1, e_cent, f1_cent);
-       sub_v3_v3v3(d2, f2_cent, e_cent);
-       cost = len_v3(d1) + len_v3(d2);
-
-       /* but is biased to give higher values to sharp turns, so that it will take
-        * paths with fewer "turns" when selecting between equal-weighted paths between
-        * the two edges */
-       cost = cost + 0.5f * cost * (2.0f - sqrtf(fabsf(dot_v3v3(d1, d2))));
-
-       return cost;
+       return step_cost_3_v3(f1_cent, e_cent, f2_cent);
 }
 
 static void facetag_add_adjacent(Heap *heap, BMFace *f1, BMFace **faces_prev, float *cost)
@@ -1549,14 +1692,14 @@ static int facetag_shortest_path(Scene *scene, BMesh *bm, BMFace *f_src, BMFace
        }
 
        if (f == f_dst) {
-               short allseams = 1;
+               short all_set = TRUE;
 
                /* Check whether the path is already completely tagged.
                 * if it is, the tags will be cleared instead of set. */
                f = f_dst;
                do {
                        if (!facetag_context_check(scene, bm, f)) {
-                               allseams = 0;
+                               all_set = FALSE;
                                break;
                        }
                } while ((f = faces_prev[BM_elem_index_get(f)]));
@@ -1564,7 +1707,7 @@ static int facetag_shortest_path(Scene *scene, BMesh *bm, BMFace *f_src, BMFace
                /* Follow path back and source and add or remove tags */
                f = f_dst;
                do {
-                       facetag_context_set(bm, scene, f, !allseams);
+                       facetag_context_set(bm, scene, f, !all_set);
                } while ((f = faces_prev[BM_elem_index_get(f)]));
        }
 
@@ -1575,41 +1718,41 @@ static int facetag_shortest_path(Scene *scene, BMesh *bm, BMFace *f_src, BMFace
        return 1;
 }
 
-static int mouse_mesh_shortest_path_face(bContext *C, ViewContext *vc)
+static int mouse_mesh_shortest_path_face(ViewContext *vc)
 {
        BMEditMesh *em = vc->em;
-       BMFace *f;
+       BMFace *f_dst;
        float dist = 75.0f;
 
-       f = EDBM_face_find_nearest(vc, &dist);
-       if (f) {
+       f_dst = EDBM_face_find_nearest(vc, &dist);
+       if (f_dst) {
                int path = 0;
                BMFace *f_act = BM_active_face_get(em->bm, FALSE, TRUE);
 
                if (f_act) {
-                       if (f_act != f) {
-                               if (facetag_shortest_path(vc->scene, em->bm, f_act, f)) {
+                       if (f_act != f_dst) {
+                               if (facetag_shortest_path(vc->scene, em->bm, f_act, f_dst)) {
                                        BM_select_history_remove(em->bm, f_act);
                                        path = 1;
                                }
                        }
                }
                if (path == 0) {
-                       int act = (facetag_context_check(vc->scene, em->bm, f) == 0);
-                       facetag_context_set(em->bm, vc->scene, f, act); /* switch the face option */
+                       int act = (facetag_context_check(vc->scene, em->bm, f_dst) == 0);
+                       facetag_context_set(em->bm, vc->scene, f_dst, act); /* switch the face option */
                }
 
                EDBM_selectmode_flush(em);
 
                /* even if this is selected it may not be in the selection list */
-               if (facetag_context_check(vc->scene, em->bm, f) == 0)
-                       BM_select_history_remove(em->bm, f);
+               if (facetag_context_check(vc->scene, em->bm, f_dst) == 0)
+                       BM_select_history_remove(em->bm, f_dst);
                else
-                       BM_select_history_store(em->bm, f);
+                       BM_select_history_store(em->bm, f_dst);
 
-               BM_active_face_set(em->bm, f);
+               BM_active_face_set(em->bm, f_dst);
 
-               EDBM_update_generic(C, em, FALSE);
+               EDBM_update_generic(em, FALSE, FALSE);
 
                return TRUE;
        }
@@ -1621,7 +1764,7 @@ static int mouse_mesh_shortest_path_face(bContext *C, ViewContext *vc)
 
 /* ******************* operator for edge and face tag ****************** */
 
-static int edbm_shortest_path_select_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
+static int edbm_shortest_path_select_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
 {
        ViewContext vc;
        BMEditMesh *em;
@@ -1634,7 +1777,7 @@ static int edbm_shortest_path_select_invoke(bContext *C, wmOperator *UNUSED(op),
        em = vc.em;
 
        if (em->selectmode & SCE_SELECT_EDGE) {
-               if (mouse_mesh_shortest_path_edge(C, &vc)) {
+               if (mouse_mesh_shortest_path_edge(&vc)) {
                        return OPERATOR_FINISHED;
                }
                else {
@@ -1642,7 +1785,7 @@ static int edbm_shortest_path_select_invoke(bContext *C, wmOperator *UNUSED(op),
                }
        }
        else if (em->selectmode & SCE_SELECT_FACE) {
-               if (mouse_mesh_shortest_path_face(C, &vc)) {
+               if (mouse_mesh_shortest_path_face(&vc)) {
                        return OPERATOR_FINISHED;
                }
                else {
@@ -1678,7 +1821,7 @@ void MESH_OT_select_shortest_path(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
        
        /* properties */
-       RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "");
+       RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
 }
 
 /* ************************************************** */
@@ -1788,7 +1931,7 @@ int EDBM_select_pick(bContext *C, const int mval[2], short extend, short deselec
                        vc.obedit->actcol = efa->mat_nr + 1;
                        vc.em->mat_nr = efa->mat_nr;
 
-                       WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING, NULL);
+                       WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, NULL);
 
                }
 
@@ -1880,56 +2023,146 @@ void EDBM_selectmode_convert(BMEditMesh *em, const short selectmode_old, const s
        BMFace *efa;
        BMIter iter;
 
+       /* first tag-to-select, then select --- this avoids a feedback loop */
+
        /* have to find out what the selectionmode was previously */
        if (selectmode_old == SCE_SELECT_VERTEX) {
                if (selectmode_new == SCE_SELECT_EDGE) {
-                       /* select all edges associated with every selected vertex */
-                       eed = BM_iter_new(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
-                       for (; eed; eed = BM_iter_step(&iter)) {
-                               if ((BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) ||
-                                    BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)))
-                               {
+                       /* select all edges associated with every selected vert */
+                       BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
+                               BM_elem_flag_set(eed, BM_ELEM_TAG, BM_edge_is_any_vert_flag_test(eed, BM_ELEM_SELECT));
+                       }
+
+                       BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
+                               if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
                                        BM_edge_select_set(em->bm, eed, TRUE);
                                }
                        }
                }
                else if (selectmode_new == SCE_SELECT_FACE) {
-                       BMIter liter;
-                       BMLoop *l;
-
-                       /* select all faces associated with every selected vertex */
-                       efa = BM_iter_new(&iter, em->bm, BM_FACES_OF_MESH, NULL);
-                       for (; efa; efa = BM_iter_step(&iter)) {
-                               l = BM_iter_new(&liter, em->bm, BM_LOOPS_OF_FACE, efa);
-                               for (; l; l = BM_iter_step(&liter)) {
-                                       if (BM_elem_flag_test(l->v, BM_ELEM_SELECT)) {
-                                               BM_face_select_set(em->bm, efa, TRUE);
-                                               break;
-                                       }
+                       /* select all faces associated with every selected vert */
+                       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                               BM_elem_flag_set(efa, BM_ELEM_TAG, BM_face_is_any_vert_flag_test(efa, BM_ELEM_SELECT));
+                       }
+
+                       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                               if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
+                                       BM_face_select_set(em->bm, efa, TRUE);
                                }
                        }
                }
        }
        else if (selectmode_old == SCE_SELECT_EDGE) {
                if (selectmode_new == SCE_SELECT_FACE) {
-                       BMIter liter;
-                       BMLoop *l;
-
-                       /* select all faces associated with every selected vertex */
-                       efa = BM_iter_new(&iter, em->bm, BM_FACES_OF_MESH, NULL);
-                       for (; efa; efa = BM_iter_step(&iter)) {
-                               l = BM_iter_new(&liter, em->bm, BM_LOOPS_OF_FACE, efa);
-                               for (; l; l = BM_iter_step(&liter)) {
-                                       if (BM_elem_flag_test(l->v, BM_ELEM_SELECT)) {
-                                               BM_face_select_set(em->bm, efa, TRUE);
-                                               break;
-                                       }
+                       /* select all faces associated with every selected edge */
+                       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                               BM_elem_flag_set(efa, BM_ELEM_TAG, BM_face_is_any_edge_flag_test(efa, BM_ELEM_SELECT));
+                       }
+
+                       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                               if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
+                                       BM_face_select_set(em->bm, efa, TRUE);
                                }
                        }
                }
        }
 }
 
+/* user facing function, does notification and undo push */
+int EDBM_selectmode_toggle(bContext *C, const short selectmode_new,
+                           const int action, const int use_extend, const int use_expand)
+{
+       ToolSettings *ts = CTX_data_tool_settings(C);
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = NULL;
+       int ret = FALSE;
+
+       if (obedit && obedit->type == OB_MESH) {
+               em = BMEdit_FromObject(obedit);
+       }
+
+       if (em == NULL) {
+               return ret;
+       }
+
+       switch (action) {
+               case -1:
+                       /* already set */
+                       break;
+               case 0:  /* disable */
+                       /* check we have something to do */
+                       if ((em->selectmode & selectmode_new) == 0) {
+                               return FALSE;
+                       }
+                       em->selectmode &= ~selectmode_new;
+                       break;
+               case 1:  /* enable */
+                       /* check we have something to do */
+                       if ((em->selectmode & selectmode_new) != 0) {
+                               return FALSE;
+                       }
+                       em->selectmode |= selectmode_new;
+                       break;
+               case 2:  /* toggle */
+                       /* can't disable this flag if its the only one set */
+                       if (em->selectmode == selectmode_new) {
+                               return FALSE;
+                       }
+                       em->selectmode ^= selectmode_new;
+                       break;
+               default:
+                       BLI_assert(0);
+       }
+
+       switch (selectmode_new) {
+               case SCE_SELECT_VERTEX:
+                       if (use_extend == 0 || em->selectmode == 0)
+                               em->selectmode = SCE_SELECT_VERTEX;
+                       ts->selectmode = em->selectmode;
+                       EDBM_selectmode_set(em);
+                       ret = TRUE;
+                       break;
+               case SCE_SELECT_EDGE:
+                       if (use_extend == 0 || em->selectmode == 0) {
+                               if (use_expand) {
+                                       const short selmode_max = highest_order_bit_s(ts->selectmode);
+                                       if (selmode_max == SCE_SELECT_VERTEX) {
+                                               EDBM_selectmode_convert(em, selmode_max, SCE_SELECT_EDGE);
+                                       }
+                               }
+                               em->selectmode = SCE_SELECT_EDGE;
+                       }
+                       ts->selectmode = em->selectmode;
+                       EDBM_selectmode_set(em);
+                       ret = TRUE;
+                       break;
+               case SCE_SELECT_FACE:
+                       if (use_extend == 0 || em->selectmode == 0) {
+                               if (use_expand) {
+                                       const short selmode_max = highest_order_bit_s(ts->selectmode);
+                                       if (ELEM(selmode_max, SCE_SELECT_VERTEX, SCE_SELECT_EDGE)) {
+                                               EDBM_selectmode_convert(em, selmode_max, SCE_SELECT_FACE);
+                                       }
+                               }
+
+                               em->selectmode = SCE_SELECT_FACE;
+                       }
+                       ts->selectmode = em->selectmode;
+                       EDBM_selectmode_set(em);
+                       ret = TRUE;
+                       break;
+               default:
+                       BLI_assert(0);
+                       break;
+       }
+
+       if (ret == TRUE) {
+               WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+               WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
+       }
+
+       return ret;
+}
 
 void EDBM_deselect_by_material(BMEditMesh *em, const short index, const short select)
 {
@@ -2029,7 +2262,7 @@ static void linked_limit_default(bContext *C, wmOperator *op)
        }
 }
 
-static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
        Object *obedit = CTX_data_edit_object(C);
        ViewContext vc;
@@ -2077,6 +2310,8 @@ static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *
                        return OPERATOR_CANCELLED;
 
                if (limit) {
+                       /* grr, shouldn't need to alloc BMO flags here */
+                       BM_mesh_elem_toolflags_ensure(bm);
                        /* hflag no-seam --> bmo-tag */
                        BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
                                /* BMESH_TODO, don't use 'BM_ELEM_SELECT' here, its a HFLAG only! */
@@ -2090,8 +2325,7 @@ static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *
                         BMW_FLAG_TEST_HIDDEN,
                         BMW_NIL_LAY);
 
-               e = BMW_begin(&walker, efa);
-               for (; efa; efa = BMW_step(&walker)) {
+               for (efa = BMW_begin(&walker, efa); efa; efa = BMW_step(&walker)) {
                        BM_face_select_set(bm, efa, sel);
                }
                BMW_end(&walker);
@@ -2112,8 +2346,7 @@ static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *
                         BMW_FLAG_TEST_HIDDEN,
                         BMW_NIL_LAY);
 
-               e = BMW_begin(&walker, eed->v1);
-               for (; e; e = BMW_step(&walker)) {
+               for (e = BMW_begin(&walker, eed->v1); e; e = BMW_step(&walker)) {
                        BM_edge_select_set(bm, e, sel);
                }
                BMW_end(&walker);
@@ -2169,6 +2402,8 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op)
                }
 
                if (limit) {
+                       /* grr, shouldn't need to alloc BMO flags here */
+                       BM_mesh_elem_toolflags_ensure(bm);
                        BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
                                /* BMESH_TODO, don't use 'BM_ELEM_SELECT' here, its a HFLAG only! */
                                BMO_elem_flag_set(bm, e, BM_ELEM_SELECT, !BM_elem_flag_test(e, BM_ELEM_SEAM));
@@ -2182,13 +2417,16 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op)
 
                BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
                        if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
-                               e = BMW_begin(&walker, efa);
-                               for (; efa; efa = BMW_step(&walker)) {
+                               for (efa = BMW_begin(&walker, efa); efa; efa = BMW_step(&walker)) {
                                        BM_face_select_set(bm, efa, TRUE);
                                }
                        }
                }
                BMW_end(&walker);
+
+               if (limit) {
+                       BM_mesh_elem_toolflags_clear(bm);
+               }
        }
        else {
                BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
@@ -2207,16 +2445,15 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op)
 
                BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
                        if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
-                               e = BMW_begin(&walker, v);
-                               for (; e; e = BMW_step(&walker)) {
-                                       BM_vert_select_set(em->bm, e->v1, TRUE);
-                                       BM_vert_select_set(em->bm, e->v2, TRUE);
+                               for (e = BMW_begin(&walker, v); e; e = BMW_step(&walker)) {
+                                       BM_edge_select_set(em->bm, e, true);
                                }
                        }
                }
                BMW_end(&walker);
+
+               EDBM_selectmode_flush(em);
        }
-       EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX);
 
        WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit);
 
@@ -2334,6 +2571,9 @@ static void walker_deselect_nth(BMEditMesh *em, int nth, int offset, BMHeader *h
                        break;
        }
 
+       /* grr, shouldn't need to alloc BMO flags here */
+       BM_mesh_elem_toolflags_ensure(bm);
+
        /* Walker restrictions uses BMO flags, not header flags,
         * so transfer BM_ELEM_SELECT from HFlags onto a BMO flag layer. */
        BMO_push(bm, NULL);
@@ -2458,14 +2698,15 @@ static int edbm_select_nth_exec(bContext *C, wmOperator *op)
        int nth = RNA_int_get(op->ptr, "nth");
        int offset = RNA_int_get(op->ptr, "offset");
 
-       offset = MIN2(nth, offset);
+       /* so input of offset zero ends up being (nth - 1) */
+       offset = (offset + (nth - 1)) % nth;
 
        if (edbm_deselect_nth(em, nth, offset) == 0) {
                BKE_report(op->reports, RPT_ERROR, "Mesh has no active vert/edge/face");
                return OPERATOR_CANCELLED;
        }
 
-       EDBM_update_generic(C, em, FALSE);
+       EDBM_update_generic(em, FALSE, FALSE);
 
        return OPERATOR_FINISHED;
 }
@@ -2476,7 +2717,7 @@ void MESH_OT_select_nth(wmOperatorType *ot)
        /* identifiers */
        ot->name = "Checker Deselect";
        ot->idname = "MESH_OT_select_nth";
-       ot->description = "Deselect every Nth element starting from a selected vertex, edge or face";
+       ot->description = "Deselect every Nth element starting from the active vertex, edge or face";
 
        /* api callbacks */
        ot->exec = edbm_select_nth_exec;
@@ -2652,6 +2893,9 @@ static int edbm_select_non_manifold_exec(bContext *C, wmOperator *op)
        BMEdge *e;
        BMIter iter;
 
+       if (!RNA_boolean_get(op->ptr, "extend"))
+               EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+
        /* Selects isolated verts, and edges that do not have 2 neighboring
         * faces
         */
@@ -2691,6 +2935,9 @@ void MESH_OT_select_non_manifold(wmOperatorType *ot)
        
        /* flags */
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* props */
+       RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection");
 }
 
 static int edbm_select_random_exec(bContext *C, wmOperator *op)
@@ -2755,8 +3002,60 @@ void MESH_OT_select_random(wmOperatorType *ot)
        /* props */
        RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f,
                                 "Percent", "Percentage of elements to select randomly", 0.f, 100.0f);
-       RNA_def_boolean(ot->srna, "extend", 0,
-                       "Extend Selection", "Extend selection instead of deselecting everything first");
+       RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
+}
+
+static int edbm_select_ungrouped_exec(bContext *C, wmOperator *op)
+{
+       Object *obedit = CTX_data_edit_object(C);
+       BMEditMesh *em = BMEdit_FromObject(obedit);
+       BMVert *eve;
+       BMIter iter;
+
+       if (!em->selectmode == SCE_SELECT_VERTEX) {
+               BKE_report(op->reports, RPT_ERROR, "Does not work out of vertex selection mode");
+               return OPERATOR_CANCELLED;
+       }
+
+       if (obedit->defbase.first == NULL) {
+               BKE_report(op->reports, RPT_ERROR, "No weights/vertex groups on object");
+               return OPERATOR_CANCELLED;
+       }
+
+       if (!RNA_boolean_get(op->ptr, "extend")) {
+               EDBM_flag_disable_all(em, BM_ELEM_SELECT);
+       }
+
+       BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
+               if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+                       MDeformVert *dv = CustomData_bmesh_get(&em->bm->vdata, eve->head.data, CD_MDEFORMVERT);
+                       /* no dv or dv set with no weight */
+                       if (dv == NULL || (dv && dv->dw == NULL)) {
+                               BM_vert_select_set(em->bm, eve, true);
+                       }
+               }
+       }
+
+       WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+
+       return OPERATOR_FINISHED;
+}
+
+void MESH_OT_select_ungrouped(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Select Ungrouped";
+       ot->idname = "MESH_OT_select_ungrouped";
+       ot->description = "Select vertices without a group";
+
+       /* api callbacks */
+       ot->exec = edbm_select_ungrouped_exec;
+       ot->poll = ED_operator_editmesh;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
 }
 
 static int edbm_select_next_loop_exec(bContext *C, wmOperator *UNUSED(op))