EditMesh: pass object data to notifiers
[blender-staging.git] / source / blender / editors / mesh / editmesh_select.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2004 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/mesh/editmesh_select.c
29  *  \ingroup edmesh
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_bitmap.h"
35 #include "BLI_bitmap_draw_2d.h"
36 #include "BLI_listbase.h"
37 #include "BLI_linklist.h"
38 #include "BLI_linklist_stack.h"
39 #include "BLI_math.h"
40 #include "BLI_math_bits.h"
41 #include "BLI_rand.h"
42 #include "BLI_array.h"
43
44 #include "BKE_context.h"
45 #include "BKE_report.h"
46 #include "BKE_paint.h"
47 #include "BKE_editmesh.h"
48
49 #include "IMB_imbuf_types.h"
50 #include "IMB_imbuf.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57 #include "RNA_enum_types.h"
58
59 #include "ED_mesh.h"
60 #include "ED_screen.h"
61 #include "ED_view3d.h"
62
63 #include "DNA_mesh_types.h"
64 #include "DNA_meshdata_types.h"
65 #include "DNA_object_types.h"
66
67 #include "UI_resources.h"
68
69 #include "bmesh_tools.h"
70
71 #include "mesh_intern.h"  /* own include */
72
73 /* use bmesh operator flags for a few operators */
74 #define BMO_ELE_TAG 1
75
76 /* ****************************** MIRROR **************** */
77
78 void EDBM_select_mirrored(
79         BMEditMesh *em, const int axis, const bool extend,
80         int *r_totmirr, int *r_totfail)
81 {
82         Mesh *me = (Mesh *)em->ob->data;
83         BMesh *bm = em->bm;
84         BMIter iter;
85         int totmirr = 0;
86         int totfail = 0;
87         bool use_topology = (me && (me->editflag & ME_EDIT_MIRROR_TOPO));
88
89         *r_totmirr = *r_totfail = 0;
90
91         /* select -> tag */
92         if (bm->selectmode & SCE_SELECT_VERTEX) {
93                 BMVert *v;
94                 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
95                         BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT));
96                 }
97         }
98         else if (em->selectmode & SCE_SELECT_EDGE) {
99                 BMEdge *e;
100                 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
101                         BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT));
102                 }
103         }
104         else {
105                 BMFace *f;
106                 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
107                         BM_elem_flag_set(f, BM_ELEM_TAG, BM_elem_flag_test(f, BM_ELEM_SELECT));
108                 }
109         }
110
111         EDBM_verts_mirror_cache_begin(em, axis, true, true, use_topology);
112
113         if (!extend)
114                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
115
116
117         if (bm->selectmode & SCE_SELECT_VERTEX) {
118                 BMVert *v;
119                 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
120                         if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN) && BM_elem_flag_test(v, BM_ELEM_TAG)) {
121                                 BMVert *v_mirr = EDBM_verts_mirror_get(em, v);
122                                 if (v_mirr && !BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
123                                         BM_vert_select_set(bm, v_mirr, true);
124                                         totmirr++;
125                                 }
126                                 else {
127                                         totfail++;
128                                 }
129                         }
130                 }
131         }
132         else if (em->selectmode & SCE_SELECT_EDGE) {
133                 BMEdge *e;
134                 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
135                         if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, BM_ELEM_TAG)) {
136                                 BMEdge *e_mirr = EDBM_verts_mirror_get_edge(em, e);
137                                 if (e_mirr && !BM_elem_flag_test(e_mirr, BM_ELEM_HIDDEN)) {
138                                         BM_edge_select_set(bm, e_mirr, true);
139                                         totmirr++;
140                                 }
141                                 else {
142                                         totfail++;
143                                 }
144                         }
145                 }
146         }
147         else {
148                 BMFace *f;
149                 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
150                         if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN) && BM_elem_flag_test(f, BM_ELEM_TAG)) {
151                                 BMFace *f_mirr = EDBM_verts_mirror_get_face(em, f);
152                                 if (f_mirr && !BM_elem_flag_test(f_mirr, BM_ELEM_HIDDEN)) {
153                                         BM_face_select_set(bm, f_mirr, true);
154                                         totmirr++;
155                                 }
156                                 else {
157                                         totfail++;
158                                 }
159                         }
160                 }
161         }
162
163         EDBM_verts_mirror_cache_end(em);
164
165         *r_totmirr = totmirr;
166         *r_totfail = totfail;
167 }
168
169 void EDBM_automerge(Scene *scene, Object *obedit, bool update, const char hflag)
170 {
171         bool ok;
172         BMEditMesh *em = BKE_editmesh_from_object(obedit);
173
174         ok = BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS,
175                           "automerge verts=%hv dist=%f",
176                           hflag, scene->toolsettings->doublimit);
177
178         if (LIKELY(ok) && update) {
179                 EDBM_update_generic(em, true, true);
180         }
181 }
182
183 /* ****************************** SELECTION ROUTINES **************** */
184
185 unsigned int bm_solidoffs = 0, bm_wireoffs = 0, bm_vertoffs = 0;    /* set in drawobject.c ... for colorindices */
186
187 /* facilities for border select and circle select */
188 static BLI_bitmap *selbuf = NULL;
189
190 static BLI_bitmap *edbm_backbuf_alloc(const int size)
191 {
192         return BLI_BITMAP_NEW(size, "selbuf");
193 }
194
195 /* reads rect, and builds selection array for quick lookup */
196 /* returns if all is OK */
197 bool EDBM_backbuf_border_init(ViewContext *vc, short xmin, short ymin, short xmax, short ymax)
198 {
199         struct ImBuf *buf;
200         unsigned int *dr;
201         int a;
202         
203         if (vc->obedit == NULL || !V3D_IS_ZBUF(vc->v3d)) {
204                 return false;
205         }
206         
207         buf = ED_view3d_backbuf_read(vc, xmin, ymin, xmax, ymax);
208         if ((buf == NULL) || (bm_vertoffs == 0)) {
209                 return false;
210         }
211
212         dr = buf->rect;
213         
214         /* build selection lookup */
215         selbuf = edbm_backbuf_alloc(bm_vertoffs + 1);
216         
217         a = (xmax - xmin + 1) * (ymax - ymin + 1);
218         while (a--) {
219                 if (*dr > 0 && *dr <= bm_vertoffs) {
220                         BLI_BITMAP_ENABLE(selbuf, *dr);
221                 }
222                 dr++;
223         }
224         IMB_freeImBuf(buf);
225         return true;
226 }
227
228 bool EDBM_backbuf_check(unsigned int index)
229 {
230         /* odd logic, if selbuf is NULL we assume no zbuf-selection is enabled
231          * and just ignore the depth buffer, this is error prone since its possible
232          * code doesn't set the depth buffer by accident, but leave for now. - Campbell */
233         if (selbuf == NULL)
234                 return true;
235
236         if (index > 0 && index <= bm_vertoffs)
237                 return BLI_BITMAP_TEST_BOOL(selbuf, index);
238
239         return false;
240 }
241
242 void EDBM_backbuf_free(void)
243 {
244         if (selbuf) MEM_freeN(selbuf);
245         selbuf = NULL;
246 }
247
248 struct LassoMaskData {
249         unsigned int *px;
250         int width;
251 };
252
253 static void edbm_mask_lasso_px_cb(int x, int x_end, int y, void *user_data)
254 {
255         struct LassoMaskData *data = user_data;
256         unsigned int *px = &data->px[(y * data->width) + x];
257         do {
258                 *px = true;
259                 px++;
260         } while (++x != x_end);
261 }
262
263
264 /* mcords is a polygon mask
265  * - grab backbuffer,
266  * - draw with black in backbuffer, 
267  * - grab again and compare
268  * returns 'OK' 
269  */
270 bool EDBM_backbuf_border_mask_init(ViewContext *vc, const int mcords[][2], short tot, short xmin, short ymin, short xmax, short ymax)
271 {
272         unsigned int *dr, *dr_mask, *dr_mask_arr;
273         struct ImBuf *buf;
274         int a;
275         struct LassoMaskData lasso_mask_data;
276         
277         /* method in use for face selecting too */
278         if (vc->obedit == NULL) {
279                 if (!BKE_paint_select_elem_test(vc->obact)) {
280                         return false;
281                 }
282         }
283         else if (!V3D_IS_ZBUF(vc->v3d)) {
284                 return false;
285         }
286
287         buf = ED_view3d_backbuf_read(vc, xmin, ymin, xmax, ymax);
288         if ((buf == NULL) || (bm_vertoffs == 0)) {
289                 return false;
290         }
291
292         dr = buf->rect;
293
294         dr_mask = dr_mask_arr = MEM_callocN(sizeof(*dr_mask) * buf->x * buf->y, __func__);
295         lasso_mask_data.px = dr_mask;
296         lasso_mask_data.width = (xmax - xmin) + 1;
297
298         BLI_bitmap_draw_2d_poly_v2i_n(
299                xmin, ymin, xmax + 1, ymax + 1,
300                mcords, tot,
301                edbm_mask_lasso_px_cb, &lasso_mask_data);
302
303         /* build selection lookup */
304         selbuf = edbm_backbuf_alloc(bm_vertoffs + 1);
305         
306         a = (xmax - xmin + 1) * (ymax - ymin + 1);
307         while (a--) {
308                 if (*dr > 0 && *dr <= bm_vertoffs && *dr_mask == true) {
309                         BLI_BITMAP_ENABLE(selbuf, *dr);
310                 }
311                 dr++; dr_mask++;
312         }
313         IMB_freeImBuf(buf);
314         MEM_freeN(dr_mask_arr);
315
316         return true;
317 }
318
319 /* circle shaped sample area */
320 bool EDBM_backbuf_circle_init(ViewContext *vc, short xs, short ys, short rads)
321 {
322         struct ImBuf *buf;
323         unsigned int *dr;
324         short xmin, ymin, xmax, ymax, xc, yc;
325         int radsq;
326         
327         /* method in use for face selecting too */
328         if (vc->obedit == NULL) {
329                 if (!BKE_paint_select_elem_test(vc->obact)) {
330                         return false;
331                 }
332         }
333         else if (!V3D_IS_ZBUF(vc->v3d)) {
334                 return false;
335         }
336
337         xmin = xs - rads; xmax = xs + rads;
338         ymin = ys - rads; ymax = ys + rads;
339         buf = ED_view3d_backbuf_read(vc, xmin, ymin, xmax, ymax);
340         if ((buf == NULL) || (bm_vertoffs == 0)) {
341                 return false;
342         }
343
344         dr = buf->rect;
345         
346         /* build selection lookup */
347         selbuf = edbm_backbuf_alloc(bm_vertoffs + 1);
348         radsq = rads * rads;
349         for (yc = -rads; yc <= rads; yc++) {
350                 for (xc = -rads; xc <= rads; xc++, dr++) {
351                         if (xc * xc + yc * yc < radsq) {
352                                 if (*dr > 0 && *dr <= bm_vertoffs) {
353                                         BLI_BITMAP_ENABLE(selbuf, *dr);
354                                 }
355                         }
356                 }
357         }
358
359         IMB_freeImBuf(buf);
360         return true;
361         
362 }
363
364
365 /* -------------------------------------------------------------------- */
366
367 /** \name Find Nearest Vert/Edge/Face
368  *
369  * \note Screen-space manhatten distances are used here,
370  * since its faster and good enough for the purpose of selection.
371  *
372  * \note \a dist_bias is used so we can bias against selected items.
373  * when choosing between elements of a single type, but return the real distance
374  * to avoid the bias interfering with distance comparisons when mixing types.
375  * \{ */
376
377 #define FIND_NEAR_SELECT_BIAS 5
378 #define FIND_NEAR_CYCLE_THRESHOLD_MIN 3
379
380 struct NearestVertUserData_Hit {
381         float   dist;
382         float   dist_bias;
383         int     index;
384         BMVert *vert;
385 };
386
387 struct NearestVertUserData {
388         float mval_fl[2];
389         bool use_select_bias;
390         bool use_cycle;
391         int cycle_index_prev;
392
393         struct NearestVertUserData_Hit hit;
394         struct NearestVertUserData_Hit hit_cycle;
395 };
396
397 static void findnearestvert__doClosest(void *userData, BMVert *eve, const float screen_co[2], int index)
398 {
399         struct NearestVertUserData *data = userData;
400         float dist_test, dist_test_bias;
401
402         dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
403
404         if (data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
405                 dist_test_bias += FIND_NEAR_SELECT_BIAS;
406         }
407
408         if (dist_test_bias < data->hit.dist_bias) {
409                 data->hit.dist_bias = dist_test_bias;
410                 data->hit.dist = dist_test;
411                 data->hit.index = index;
412                 data->hit.vert = eve;
413         }
414
415         if (data->use_cycle) {
416                 if ((data->hit_cycle.vert == NULL) &&
417                     (index > data->cycle_index_prev) &&
418                     (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
419                 {
420                         data->hit_cycle.dist_bias = dist_test_bias;
421                         data->hit_cycle.dist = dist_test;
422                         data->hit_cycle.index = index;
423                         data->hit_cycle.vert = eve;
424                 }
425         }
426 }
427
428 /**
429  * Nearest vertex under the cursor.
430  *
431  * \param r_dist (in/out), minimal distance to the nearest and at the end, actual distance
432  * \param use_select_bias
433  * - When true, selected vertice are given a 5 pixel bias to make them further than unselect verts.
434  * - When false, unselected vertice are given the bias.
435  * \param use_cycle Cycle over elements within #FIND_NEAR_CYCLE_THRESHOLD_MIN in order of index.
436  */
437 BMVert *EDBM_vert_find_nearest_ex(
438         ViewContext *vc, float *r_dist,
439         const bool use_select_bias, bool use_cycle)
440 {
441         BMesh *bm = vc->em->bm;
442
443         if (V3D_IS_ZBUF(vc->v3d)) {
444                 const int dist_px = ED_view3d_backbuf_sample_size_clamp(vc->ar, *r_dist);
445                 float dist_test;
446                 unsigned int index;
447                 BMVert *eve;
448                 
449                 /* No afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */
450                 ED_view3d_backbuf_validate(vc);
451
452                 index = ED_view3d_backbuf_sample_rect(
453                         vc, vc->mval, dist_px, bm_wireoffs, 0xFFFFFF, &dist_test);
454                 eve = index ? BM_vert_at_index_find_or_table(bm, index - 1) : NULL;
455                 
456                 if (eve) {
457                         if (dist_test < *r_dist) {
458                                 *r_dist = dist_test;
459                                 return eve;
460                         }
461                 }
462                 return NULL;
463         }
464         else {
465                 struct NearestVertUserData data = {{0}};
466                 const struct NearestVertUserData_Hit *hit;
467                 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT;
468
469                 static int prev_select_index = 0;
470                 static const BMVert *prev_select_elem = NULL;
471
472                 if ((use_cycle == false) ||
473                     (prev_select_elem && (prev_select_elem != BM_vert_at_index_find_or_table(bm, prev_select_index))))
474                 {
475                         prev_select_index = 0;
476                         prev_select_elem = NULL;
477                 }
478
479                 data.mval_fl[0] = vc->mval[0];
480                 data.mval_fl[1] = vc->mval[1];
481                 data.use_select_bias = use_select_bias;
482                 data.use_cycle = use_cycle;
483                 data.hit.dist      = data.hit_cycle.dist = \
484                 data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
485                 data.cycle_index_prev = prev_select_index;
486
487                 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
488                 mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, clip_flag);
489
490                 hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit;
491                 *r_dist = hit->dist;
492
493                 prev_select_elem = hit->vert;
494                 prev_select_index = hit->index;
495
496                 return hit->vert;
497         }
498 }
499
500 BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *r_dist)
501 {
502         return EDBM_vert_find_nearest_ex(vc, r_dist, false, false);
503 }
504
505 /* find the distance to the edge we already have */
506 struct NearestEdgeUserData_ZBuf {
507         float mval_fl[2];
508         float dist;
509         const BMEdge *edge_test;
510 };
511
512 static void find_nearest_edge_center__doZBuf(
513         void *userData, BMEdge *eed,
514         const float screen_co_a[2], const float screen_co_b[2],
515         int UNUSED(index))
516 {
517         struct NearestEdgeUserData_ZBuf *data = userData;
518
519         if (eed == data->edge_test) {
520                 float dist_test;
521                 float screen_co_mid[2];
522
523                 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
524                 dist_test = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
525
526                 if (dist_test < data->dist) {
527                         data->dist = dist_test;
528                 }
529         }
530 }
531
532 struct NearestEdgeUserData_Hit {
533         float   dist;
534         float   dist_bias;
535         int     index;
536         BMEdge *edge;
537
538         /* edges only, un-biased manhatten distance to which ever edge we pick
539          * (not used for choosing) */
540         float   dist_center;
541 };
542
543 struct NearestEdgeUserData {
544         ViewContext vc;
545         float mval_fl[2];
546         bool use_select_bias;
547         bool use_cycle;
548         int cycle_index_prev;
549
550         struct NearestEdgeUserData_Hit hit;
551         struct NearestEdgeUserData_Hit hit_cycle;
552 };
553
554 /* note; uses v3d, so needs active 3d window */
555 static void find_nearest_edge__doClosest(
556         void *userData, BMEdge *eed,
557         const float screen_co_a[2], const float screen_co_b[2],
558         int index)
559 {
560         struct NearestEdgeUserData *data = userData;
561         float dist_test, dist_test_bias;
562
563         float fac = line_point_factor_v2(data->mval_fl, screen_co_a, screen_co_b);
564         float screen_co[2];
565
566         if (fac <= 0.0f) {
567                 fac = 0.0f;
568                 copy_v2_v2(screen_co, screen_co_a);
569         }
570         else if (fac >= 1.0f) {
571                 fac = 1.0f;
572                 copy_v2_v2(screen_co, screen_co_b);
573         }
574         else {
575                 interp_v2_v2v2(screen_co, screen_co_a, screen_co_b, fac);
576         }
577
578         dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
579
580         if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
581                 dist_test_bias += FIND_NEAR_SELECT_BIAS;
582         }
583
584         if (data->vc.rv3d->rflag & RV3D_CLIPPING) {
585                 float vec[3];
586
587                 interp_v3_v3v3(vec, eed->v1->co, eed->v2->co, fac);
588                 if (ED_view3d_clipping_test(data->vc.rv3d, vec, true)) {
589                         return;
590                 }
591         }
592
593         if (dist_test_bias < data->hit.dist_bias) {
594                 float screen_co_mid[2];
595
596                 data->hit.dist_bias = dist_test_bias;
597                 data->hit.dist = dist_test;
598                 data->hit.index = index;
599                 data->hit.edge = eed;
600
601                 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
602                 data->hit.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
603         }
604
605         if (data->use_cycle) {
606                 if ((data->hit_cycle.edge == NULL) &&
607                     (index > data->cycle_index_prev) &&
608                     (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
609                 {
610                         float screen_co_mid[2];
611
612                         data->hit_cycle.dist_bias = dist_test_bias;
613                         data->hit_cycle.dist = dist_test;
614                         data->hit_cycle.index = index;
615                         data->hit_cycle.edge = eed;
616
617                         mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
618                         data->hit_cycle.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
619                 }
620         }
621 }
622
623 BMEdge *EDBM_edge_find_nearest_ex(
624         ViewContext *vc, float *r_dist,
625         float *r_dist_center,
626         const bool use_select_bias, const bool use_cycle,
627         BMEdge **r_eed_zbuf)
628 {
629         BMesh *bm = vc->em->bm;
630
631         if (V3D_IS_ZBUF(vc->v3d)) {
632                 const int dist_px = ED_view3d_backbuf_sample_size_clamp(vc->ar, *r_dist);
633                 float dist_test = 0.0f;
634                 unsigned int index;
635                 BMEdge *eed;
636
637                 /* Make sure that the edges are considered for selection.
638                  * TODO: cleanup: add `selectmode` as a parameter */
639                 const short ts_selectmode = vc->scene->toolsettings->selectmode;
640                 vc->scene->toolsettings->selectmode |= SCE_SELECT_EDGE;
641
642                 /* No afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */
643                 ED_view3d_backbuf_validate(vc);
644
645                 /* restore `selectmode` */
646                 vc->scene->toolsettings->selectmode = ts_selectmode;
647
648                 index = ED_view3d_backbuf_sample_rect(vc, vc->mval, dist_px, bm_solidoffs, bm_wireoffs, &dist_test);
649                 eed = index ? BM_edge_at_index_find_or_table(bm, index - 1) : NULL;
650
651                 if (r_eed_zbuf) {
652                         *r_eed_zbuf = eed;
653                 }
654
655                 /* exception for faces (verts don't need this) */
656                 if (r_dist_center && eed) {
657                         struct NearestEdgeUserData_ZBuf data;
658
659                         data.mval_fl[0] = vc->mval[0];
660                         data.mval_fl[1] = vc->mval[1];
661                         data.dist = FLT_MAX;
662                         data.edge_test = eed;
663
664                         ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
665
666                         mesh_foreachScreenEdge(vc, find_nearest_edge_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
667
668                         *r_dist_center = data.dist;
669                 }
670                 /* end exception */
671
672                 if (eed) {
673                         if (dist_test < *r_dist) {
674                                 *r_dist = dist_test;
675                                 return eed;
676                         }
677                 }
678                 return NULL;
679         }
680         else {
681                 struct NearestEdgeUserData data = {{0}};
682                 const struct NearestEdgeUserData_Hit *hit;
683                 /* interpolate along the edge before doing a clipping plane test */
684                 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB;
685
686                 static int prev_select_index = 0;
687                 static const BMEdge *prev_select_elem = NULL;
688
689                 if ((use_cycle == false) ||
690                     (prev_select_elem && (prev_select_elem != BM_edge_at_index_find_or_table(bm, prev_select_index))))
691                 {
692                         prev_select_index = 0;
693                         prev_select_elem = NULL;
694                 }
695
696                 data.vc = *vc;
697                 data.mval_fl[0] = vc->mval[0];
698                 data.mval_fl[1] = vc->mval[1];
699                 data.use_select_bias = use_select_bias;
700                 data.use_cycle = use_cycle;
701                 data.hit.dist      = data.hit_cycle.dist = \
702                 data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
703                 data.cycle_index_prev = prev_select_index;
704
705                 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
706                 mesh_foreachScreenEdge(vc, find_nearest_edge__doClosest, &data, clip_flag);
707
708                 hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit;
709                 *r_dist = hit->dist;
710                 if (r_dist_center) {
711                         *r_dist_center = hit->dist_center;
712                 }
713
714                 prev_select_elem = hit->edge;
715                 prev_select_index = hit->index;
716
717                 return hit->edge;
718         }
719 }
720
721 BMEdge *EDBM_edge_find_nearest(
722         ViewContext *vc, float *r_dist)
723 {
724         return EDBM_edge_find_nearest_ex(vc, r_dist, NULL, false, false, NULL);
725 }
726
727 /* find the distance to the face we already have */
728 struct NearestFaceUserData_ZBuf {
729         float mval_fl[2];
730         float dist;
731         const BMFace *face_test;
732 };
733
734 static void find_nearest_face_center__doZBuf(void *userData, BMFace *efa, const float screen_co[2], int UNUSED(index))
735 {
736         struct NearestFaceUserData_ZBuf *data = userData;
737
738         if (efa == data->face_test) {
739                 const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co);
740
741                 if (dist_test < data->dist) {
742                         data->dist = dist_test;
743                 }
744         }
745 }
746
747
748 struct NearestFaceUserData_Hit {
749         float   dist;
750         float   dist_bias;
751         int     index;
752         BMFace *face;
753 };
754
755 struct NearestFaceUserData {
756         float mval_fl[2];
757         bool use_select_bias;
758         bool use_cycle;
759         int cycle_index_prev;
760
761         struct NearestFaceUserData_Hit hit;
762         struct NearestFaceUserData_Hit hit_cycle;
763 };
764
765 static void findnearestface__doClosest(void *userData, BMFace *efa, const float screen_co[2], int index)
766 {
767         struct NearestFaceUserData *data = userData;
768         float dist_test, dist_test_bias;
769
770         dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
771
772         if (data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
773                 dist_test_bias += FIND_NEAR_SELECT_BIAS;
774         }
775
776         if (dist_test_bias < data->hit.dist_bias) {
777                 data->hit.dist_bias = dist_test_bias;
778                 data->hit.dist = dist_test;
779                 data->hit.index = index;
780                 data->hit.face = efa;
781         }
782
783         if (data->use_cycle) {
784                 if ((data->hit_cycle.face == NULL) &&
785                     (index > data->cycle_index_prev) &&
786                     (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
787                 {
788                         data->hit_cycle.dist_bias = dist_test_bias;
789                         data->hit_cycle.dist = dist_test;
790                         data->hit_cycle.index = index;
791                         data->hit_cycle.face = efa;
792                 }
793         }
794 }
795
796
797 BMFace *EDBM_face_find_nearest_ex(
798         ViewContext *vc, float *r_dist,
799         float *r_dist_center,
800         const bool use_select_bias, const bool use_cycle,
801         BMFace **r_efa_zbuf)
802 {
803         BMesh *bm = vc->em->bm;
804
805         if (V3D_IS_ZBUF(vc->v3d)) {
806                 float dist_test = 0.0f;
807                 unsigned int index;
808                 BMFace *efa;
809
810                 ED_view3d_backbuf_validate(vc);
811
812                 index = ED_view3d_backbuf_sample(vc, vc->mval[0], vc->mval[1]);
813                 efa = index ? BM_face_at_index_find_or_table(bm, index - 1) : NULL;
814                 
815                 if (r_efa_zbuf) {
816                         *r_efa_zbuf = efa;
817                 }
818
819                 /* exception for faces (verts don't need this) */
820                 if (r_dist_center && efa) {
821                         struct NearestFaceUserData_ZBuf data;
822
823                         data.mval_fl[0] = vc->mval[0];
824                         data.mval_fl[1] = vc->mval[1];
825                         data.dist = FLT_MAX;
826                         data.face_test = efa;
827
828                         ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
829
830                         mesh_foreachScreenFace(vc, find_nearest_face_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
831
832                         *r_dist_center = data.dist;
833                 }
834                 /* end exception */
835
836                 if (efa) {
837                         if (dist_test < *r_dist) {
838                                 *r_dist = dist_test;
839                                 return efa;
840                         }
841                 }
842                 return NULL;
843         }
844         else {
845                 struct NearestFaceUserData data = {{0}};
846                 const struct NearestFaceUserData_Hit *hit;
847                 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT;
848
849                 static int prev_select_index = 0;
850                 static const BMFace *prev_select_elem = NULL;
851
852                 if ((use_cycle == false) ||
853                     (prev_select_elem && (prev_select_elem != BM_face_at_index_find_or_table(bm, prev_select_index))))
854                 {
855                         prev_select_index = 0;
856                         prev_select_elem = NULL;
857                 }
858
859                 data.mval_fl[0] = vc->mval[0];
860                 data.mval_fl[1] = vc->mval[1];
861                 data.use_select_bias = use_select_bias;
862                 data.use_cycle = use_cycle;
863                 data.hit.dist      = data.hit_cycle.dist = \
864                 data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist;
865                 data.cycle_index_prev = prev_select_index;
866
867                 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
868                 mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag);
869
870                 hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit;
871                 *r_dist = hit->dist;
872                 if (r_dist_center) {
873                         *r_dist_center = hit->dist;
874                 }
875
876                 prev_select_elem = hit->face;
877                 prev_select_index = hit->index;
878
879                 return hit->face;
880         }
881 }
882
883 BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist)
884 {
885         return EDBM_face_find_nearest_ex(vc, r_dist, NULL, false, false, NULL);
886 }
887
888 #undef FIND_NEAR_SELECT_BIAS
889 #undef FIND_NEAR_CYCLE_THRESHOLD_MIN
890
891
892 /* best distance based on screen coords. 
893  * use em->selectmode to define how to use 
894  * selected vertices and edges get disadvantage
895  * return 1 if found one
896  */
897 static int unified_findnearest(ViewContext *vc, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
898 {
899         BMEditMesh *em = vc->em;
900         static short mval_prev[2] = {-1, -1};
901         /* only cycle while the mouse remains still */
902         const bool use_cycle = ((mval_prev[0] == vc->mval[0]) && (mval_prev[1] == vc->mval[1]));
903         const float dist_init = ED_view3d_select_dist_px();
904         /* since edges select lines, we give dots advantage of ~20 pix */
905         const float dist_margin = (dist_init / 2);
906         float dist = dist_init;
907         BMFace *efa_zbuf = NULL;
908         BMEdge *eed_zbuf = NULL;
909         
910         BMVert *eve = NULL;
911         BMEdge *eed = NULL;
912         BMFace *efa = NULL;
913
914
915         /* no afterqueue (yet), so we check it now, otherwise the em_xxxofs indices are bad */
916         ED_view3d_backbuf_validate(vc);
917
918         if ((dist > 0.0f) && em->selectmode & SCE_SELECT_FACE) {
919                 float dist_center = 0.0f;
920                 float *dist_center_p = (em->selectmode & (SCE_SELECT_EDGE | SCE_SELECT_VERTEX)) ? &dist_center : NULL;
921                 efa = EDBM_face_find_nearest_ex(vc, &dist, dist_center_p, true, use_cycle, &efa_zbuf);
922                 if (efa && dist_center_p) {
923                         dist = min_ff(dist_margin, dist_center);
924                 }
925         }
926
927         if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_EDGE)) {
928                 float dist_center = 0.0f;
929                 float *dist_center_p = (em->selectmode & SCE_SELECT_VERTEX) ? &dist_center : NULL;
930                 eed = EDBM_edge_find_nearest_ex(vc, &dist, dist_center_p, true, use_cycle, &eed_zbuf);
931                 if (eed && dist_center_p) {
932                         dist = min_ff(dist_margin, dist_center);
933                 }
934         }
935
936         if ((dist > 0.0f) && em->selectmode & SCE_SELECT_VERTEX) {
937                 eve = EDBM_vert_find_nearest_ex(vc, &dist, true, use_cycle);
938         }
939
940         /* return only one of 3 pointers, for frontbuffer redraws */
941         if (eve) {
942                 efa = NULL; eed = NULL;
943         }
944         else if (eed) {
945                 efa = NULL;
946         }
947
948         /* there may be a face under the cursor, who's center if too far away
949          * use this if all else fails, it makes sense to select this */
950         if ((eve || eed || efa) == 0) {
951                 if (eed_zbuf) {
952                         eed = eed_zbuf;
953                 }
954                 else if (efa_zbuf) {
955                         efa = efa_zbuf;
956                 }
957         }
958
959         mval_prev[0] = vc->mval[0];
960         mval_prev[1] = vc->mval[1];
961
962         *r_eve = eve;
963         *r_eed = eed;
964         *r_efa = efa;
965
966         return (eve || eed || efa);
967 }
968
969 /** \} */
970
971
972 /* ****************  SIMILAR "group" SELECTS. FACE, EDGE AND VERTEX ************** */
973 static const EnumPropertyItem prop_similar_compare_types[] = {
974         {SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
975         {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
976         {SIM_CMP_LT, "LESS", 0, "Less", ""},
977
978         {0, NULL, 0, NULL, NULL}
979 };
980
981 static const EnumPropertyItem prop_similar_types[] = {
982         {SIMVERT_NORMAL, "NORMAL", 0, "Normal", ""},
983         {SIMVERT_FACE, "FACE", 0, "Amount of Adjacent Faces", ""},
984         {SIMVERT_VGROUP, "VGROUP", 0, "Vertex Groups", ""},
985         {SIMVERT_EDGE, "EDGE", 0, "Amount of connecting edges", ""},
986
987         {SIMEDGE_LENGTH, "LENGTH", 0, "Length", ""},
988         {SIMEDGE_DIR, "DIR", 0, "Direction", ""},
989         {SIMEDGE_FACE, "FACE", 0, "Amount of Faces Around an Edge", ""},
990         {SIMEDGE_FACE_ANGLE, "FACE_ANGLE", 0, "Face Angles", ""},
991         {SIMEDGE_CREASE, "CREASE", 0, "Crease", ""},
992         {SIMEDGE_BEVEL, "BEVEL", 0, "Bevel", ""},
993         {SIMEDGE_SEAM, "SEAM", 0, "Seam", ""},
994         {SIMEDGE_SHARP, "SHARP", 0, "Sharpness", ""},
995 #ifdef WITH_FREESTYLE
996         {SIMEDGE_FREESTYLE, "FREESTYLE_EDGE", 0, "Freestyle Edge Marks", ""},
997 #endif
998
999         {SIMFACE_MATERIAL, "MATERIAL", 0, "Material", ""},
1000         {SIMFACE_IMAGE, "IMAGE", 0, "Image", ""},
1001         {SIMFACE_AREA, "AREA", 0, "Area", ""},
1002         {SIMFACE_SIDES, "SIDES", 0, "Polygon Sides", ""},
1003         {SIMFACE_PERIMETER, "PERIMETER", 0, "Perimeter", ""},
1004         {SIMFACE_NORMAL, "NORMAL", 0, "Normal", ""},
1005         {SIMFACE_COPLANAR, "COPLANAR", 0, "Co-planar", ""},
1006         {SIMFACE_SMOOTH, "SMOOTH", 0, "Flat/Smooth", ""},
1007 #ifdef WITH_FREESTYLE
1008         {SIMFACE_FREESTYLE, "FREESTYLE_FACE", 0, "Freestyle Face Marks", ""},
1009 #endif
1010
1011         {0, NULL, 0, NULL, NULL}
1012 };
1013
1014 /* selects new faces/edges/verts based on the existing selection */
1015
1016 static int similar_face_select_exec(bContext *C, wmOperator *op)
1017 {
1018         Object *ob = CTX_data_edit_object(C);
1019         BMEditMesh *em = BKE_editmesh_from_object(ob);
1020         BMOperator bmop;
1021
1022         /* get the type from RNA */
1023         const int type = RNA_enum_get(op->ptr, "type");
1024         const float thresh = RNA_float_get(op->ptr, "threshold");
1025         const int compare = RNA_enum_get(op->ptr, "compare");
1026
1027         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
1028         EDBM_op_init(em, &bmop, op,
1029                      "similar_faces faces=%hf type=%i thresh=%f compare=%i",
1030                      BM_ELEM_SELECT, type, thresh, compare);
1031
1032         /* execute the operator */
1033         BMO_op_exec(em->bm, &bmop);
1034
1035         /* clear the existing selection */
1036         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1037
1038         /* select the output */
1039         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
1040
1041         /* finish the operator */
1042         if (!EDBM_op_finish(em, &bmop, op, true)) {
1043                 return OPERATOR_CANCELLED;
1044         }
1045
1046         EDBM_update_generic(em, false, false);
1047
1048         return OPERATOR_FINISHED;
1049 }       
1050
1051 /* ***************************************************** */
1052
1053 /* EDGE GROUP */
1054
1055 /* wrap the above function but do selection flushing edge to face */
1056 static int similar_edge_select_exec(bContext *C, wmOperator *op)
1057 {
1058         Object *ob = CTX_data_edit_object(C);
1059         BMEditMesh *em = BKE_editmesh_from_object(ob);
1060         BMOperator bmop;
1061
1062         /* get the type from RNA */
1063         const int type = RNA_enum_get(op->ptr, "type");
1064         const float thresh = RNA_float_get(op->ptr, "threshold");
1065         const int compare = RNA_enum_get(op->ptr, "compare");
1066
1067         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
1068         EDBM_op_init(em, &bmop, op,
1069                      "similar_edges edges=%he type=%i thresh=%f compare=%i",
1070                      BM_ELEM_SELECT, type, thresh, compare);
1071
1072         /* execute the operator */
1073         BMO_op_exec(em->bm, &bmop);
1074
1075         /* clear the existing selection */
1076         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1077
1078         /* select the output */
1079         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
1080         EDBM_selectmode_flush(em);
1081
1082         /* finish the operator */
1083         if (!EDBM_op_finish(em, &bmop, op, true)) {
1084                 return OPERATOR_CANCELLED;
1085         }
1086
1087         EDBM_update_generic(em, false, false);
1088
1089         return OPERATOR_FINISHED;
1090 }
1091
1092 /* ********************************* */
1093
1094 /*
1095  * VERT GROUP
1096  * mode 1: same normal
1097  * mode 2: same number of face users
1098  * mode 3: same vertex groups
1099  */
1100 static int similar_vert_select_exec(bContext *C, wmOperator *op)
1101 {
1102         Object *ob = CTX_data_edit_object(C);
1103         BMEditMesh *em = BKE_editmesh_from_object(ob);
1104         BMOperator bmop;
1105         /* get the type from RNA */
1106         const int type = RNA_enum_get(op->ptr, "type");
1107         const float thresh = RNA_float_get(op->ptr, "threshold");
1108         const int compare = RNA_enum_get(op->ptr, "compare");
1109
1110         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
1111         EDBM_op_init(em, &bmop, op,
1112                      "similar_verts verts=%hv type=%i thresh=%f compare=%i",
1113                      BM_ELEM_SELECT, type, thresh, compare);
1114
1115         /* execute the operator */
1116         BMO_op_exec(em->bm, &bmop);
1117
1118         /* clear the existing selection */
1119         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1120
1121         /* select the output */
1122         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true);
1123
1124         /* finish the operator */
1125         if (!EDBM_op_finish(em, &bmop, op, true)) {
1126                 return OPERATOR_CANCELLED;
1127         }
1128
1129         EDBM_selectmode_flush(em);
1130
1131         EDBM_update_generic(em, false, false);
1132
1133         return OPERATOR_FINISHED;
1134 }
1135
1136 static int edbm_select_similar_exec(bContext *C, wmOperator *op)
1137 {
1138         ToolSettings *ts = CTX_data_tool_settings(C);
1139         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold");
1140
1141         const int type = RNA_enum_get(op->ptr, "type");
1142
1143         if (!RNA_property_is_set(op->ptr, prop)) {
1144                 RNA_property_float_set(op->ptr, prop, ts->select_thresh);
1145         }
1146         else {
1147                 ts->select_thresh = RNA_property_float_get(op->ptr, prop);
1148         }
1149
1150         if      (type < 100) return similar_vert_select_exec(C, op);
1151         else if (type < 200) return similar_edge_select_exec(C, op);
1152         else                 return similar_face_select_exec(C, op);
1153 }
1154
1155 static const EnumPropertyItem *select_similar_type_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
1156                                                    bool *r_free)
1157 {
1158         Object *obedit;
1159
1160         if (!C) /* needed for docs and i18n tools */
1161                 return prop_similar_types;
1162
1163         obedit = CTX_data_edit_object(C);
1164
1165         if (obedit && obedit->type == OB_MESH) {
1166                 EnumPropertyItem *item = NULL;
1167                 int a, totitem = 0;
1168                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
1169
1170                 if (em->selectmode & SCE_SELECT_VERTEX) {
1171                         for (a = SIMVERT_NORMAL; a < SIMEDGE_LENGTH; a++) {
1172                                 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1173                         }
1174                 }
1175                 else if (em->selectmode & SCE_SELECT_EDGE) {
1176                         for (a = SIMEDGE_LENGTH; a < SIMFACE_MATERIAL; a++) {
1177                                 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1178                         }
1179                 }
1180                 else if (em->selectmode & SCE_SELECT_FACE) {
1181 #ifdef WITH_FREESTYLE
1182                         const int a_end = SIMFACE_FREESTYLE;
1183 #else
1184                         const int a_end = SIMFACE_SMOOTH;
1185 #endif
1186                         for (a = SIMFACE_MATERIAL; a <= a_end; a++) {
1187                                 RNA_enum_items_add_value(&item, &totitem, prop_similar_types, a);
1188                         }
1189                 }
1190                 RNA_enum_item_end(&item, &totitem);
1191
1192                 *r_free = true;
1193
1194                 return item;
1195         }
1196
1197         return prop_similar_types;
1198 }
1199
1200 void MESH_OT_select_similar(wmOperatorType *ot)
1201 {
1202         PropertyRNA *prop;
1203
1204         /* identifiers */
1205         ot->name = "Select Similar";
1206         ot->idname = "MESH_OT_select_similar";
1207         ot->description = "Select similar vertices, edges or faces by property types";
1208         
1209         /* api callbacks */
1210         ot->invoke = WM_menu_invoke;
1211         ot->exec = edbm_select_similar_exec;
1212         ot->poll = ED_operator_editmesh;
1213         
1214         /* flags */
1215         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1216         
1217         /* properties */
1218         prop = ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMVERT_NORMAL, "Type", "");
1219         RNA_def_enum_funcs(prop, select_similar_type_itemf);
1220
1221         RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
1222
1223         RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
1224 }
1225
1226
1227 /* -------------------------------------------------------------------- */
1228 /* Select Similar Regions */
1229
1230 static int edbm_select_similar_region_exec(bContext *C, wmOperator *op)
1231 {
1232         Object *obedit = CTX_data_edit_object(C);
1233         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1234         BMesh *bm = em->bm;
1235         bool changed = false;
1236
1237         /* group vars */
1238         int *groups_array;
1239         int (*group_index)[2];
1240         int group_tot;
1241         int i;
1242
1243         if (bm->totfacesel < 2) {
1244                 BKE_report(op->reports, RPT_ERROR, "No face regions selected");
1245                 return OPERATOR_CANCELLED;
1246         }
1247
1248         groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totfacesel, __func__);
1249         group_tot = BM_mesh_calc_face_groups(bm, groups_array, &group_index,
1250                                              NULL, NULL,
1251                                              BM_ELEM_SELECT, BM_VERT);
1252
1253         BM_mesh_elem_table_ensure(bm, BM_FACE);
1254
1255         for (i = 0; i < group_tot; i++) {
1256                 ListBase faces_regions;
1257                 int tot;
1258
1259                 const int fg_sta = group_index[i][0];
1260                 const int fg_len = group_index[i][1];
1261                 int j;
1262                 BMFace **fg = MEM_mallocN(sizeof(*fg) * fg_len, __func__);
1263
1264
1265                 for (j = 0; j < fg_len; j++) {
1266                         fg[j] = BM_face_at_index(bm, groups_array[fg_sta + j]);
1267                 }
1268
1269                 tot = BM_mesh_region_match(bm, fg, fg_len, &faces_regions);
1270
1271                 MEM_freeN(fg);
1272
1273                 if (tot) {
1274                         LinkData *link;
1275                         while ((link = BLI_pophead(&faces_regions))) {
1276                                 BMFace *f, **faces = link->data;
1277                                 while ((f = *(faces++))) {
1278                                         BM_face_select_set(bm, f, true);
1279                                 }
1280                                 MEM_freeN(link->data);
1281                                 MEM_freeN(link);
1282
1283                                 changed = true;
1284                         }
1285                 }
1286         }
1287
1288         MEM_freeN(groups_array);
1289         MEM_freeN(group_index);
1290
1291         if (changed) {
1292                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1293         }
1294         else {
1295                 BKE_report(op->reports, RPT_WARNING, "No matching face regions found");
1296         }
1297
1298         return OPERATOR_FINISHED;
1299 }
1300
1301 void MESH_OT_select_similar_region(wmOperatorType *ot)
1302 {
1303         /* identifiers */
1304         ot->name = "Select Similar Regions";
1305         ot->idname = "MESH_OT_select_similar_region";
1306         ot->description = "Select similar face regions to the current selection";
1307
1308         /* api callbacks */
1309         ot->exec = edbm_select_similar_region_exec;
1310         ot->poll = ED_operator_editmesh;
1311
1312         /* flags */
1313         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1314 }
1315
1316
1317 /* ****************  Mode Select *************** */
1318
1319 static int edbm_select_mode_exec(bContext *C, wmOperator *op)
1320 {
1321         const int type        = RNA_enum_get(op->ptr,    "type");
1322         const int action      = RNA_enum_get(op->ptr,    "action");
1323         const bool use_extend = RNA_boolean_get(op->ptr, "use_extend");
1324         const bool use_expand = RNA_boolean_get(op->ptr, "use_expand");
1325
1326         if (EDBM_selectmode_toggle(C, type, action, use_extend, use_expand)) {
1327                 return OPERATOR_FINISHED;
1328         }
1329         else {
1330                 return OPERATOR_CANCELLED;
1331         }
1332 }
1333
1334 static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1335 {
1336         /* detecting these options based on shift/ctrl here is weak, but it's done
1337          * to make this work when clicking buttons or menus */
1338         if (!RNA_struct_property_is_set(op->ptr, "use_extend"))
1339                 RNA_boolean_set(op->ptr, "use_extend", event->shift);
1340         if (!RNA_struct_property_is_set(op->ptr, "use_expand"))
1341                 RNA_boolean_set(op->ptr, "use_expand", event->ctrl);
1342
1343         return edbm_select_mode_exec(C, op);
1344 }
1345
1346 void MESH_OT_select_mode(wmOperatorType *ot)
1347 {
1348         PropertyRNA *prop;
1349
1350         static const EnumPropertyItem elem_items[] = {
1351                 {SCE_SELECT_VERTEX, "VERT", ICON_VERTEXSEL, "Vertices", ""},
1352                 {SCE_SELECT_EDGE,   "EDGE", ICON_EDGESEL, "Edges", ""},
1353                 {SCE_SELECT_FACE,   "FACE", ICON_FACESEL, "Faces", ""},
1354                 {0, NULL, 0, NULL, NULL},
1355         };
1356
1357         static const EnumPropertyItem actions_items[] = {
1358                 {0, "DISABLE", 0, "Disable", "Disable selected markers"},
1359                 {1, "ENABLE", 0, "Enable", "Enable selected markers"},
1360                 {2, "TOGGLE", 0, "Toggle", "Toggle disabled flag for selected markers"},
1361                 {0, NULL, 0, NULL, NULL}
1362         };
1363
1364         /* identifiers */
1365         ot->name = "Select Mode";
1366         ot->idname = "MESH_OT_select_mode";
1367         ot->description = "Change selection mode";
1368
1369         /* api callbacks */
1370         ot->invoke = edbm_select_mode_invoke;
1371         ot->exec = edbm_select_mode_exec;
1372         ot->poll = ED_operator_editmesh;
1373
1374         /* flags */
1375         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1376
1377         /* properties */
1378         prop = RNA_def_boolean(ot->srna, "use_extend", false, "Extend", "");
1379         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1380         prop = RNA_def_boolean(ot->srna, "use_expand", false, "Expand", "");
1381         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1382         ot->prop = prop = RNA_def_enum(ot->srna, "type", elem_items, 0, "Type", "");
1383         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1384
1385         RNA_def_enum(ot->srna, "action", actions_items, 2, "Action", "Selection action to execute");
1386 }
1387
1388 /* ***************************************************** */
1389
1390 /* ****************  LOOP SELECTS *************** */
1391
1392 static void walker_select_count(BMEditMesh *em, int walkercode, void *start, const bool select, const bool select_mix,
1393                                 int *r_totsel, int *r_totunsel)
1394 {
1395         BMesh *bm = em->bm;
1396         BMElem *ele;
1397         BMWalker walker;
1398         int tot[2] = {0, 0};
1399
1400         BMW_init(&walker, bm, walkercode,
1401                  BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
1402                  BMW_FLAG_TEST_HIDDEN,
1403                  BMW_NIL_LAY);
1404
1405         for (ele = BMW_begin(&walker, start); ele; ele = BMW_step(&walker)) {
1406                 tot[(BM_elem_flag_test_bool(ele, BM_ELEM_SELECT) != select)] += 1;
1407
1408                 if (!select_mix && tot[0] && tot[1]) {
1409                         tot[0] = tot[1] = -1;
1410                         break;
1411                 }
1412         }
1413
1414         *r_totsel = tot[0];
1415         *r_totunsel = tot[1];
1416
1417         BMW_end(&walker);
1418 }
1419
1420 static void walker_select(BMEditMesh *em, int walkercode, void *start, const bool select)
1421 {
1422         BMesh *bm = em->bm;
1423         BMElem *ele;
1424         BMWalker walker;
1425
1426         BMW_init(&walker, bm, walkercode,
1427                  BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
1428                  BMW_FLAG_TEST_HIDDEN,
1429                  BMW_NIL_LAY);
1430
1431         for (ele = BMW_begin(&walker, start); ele; ele = BMW_step(&walker)) {
1432                 if (!select) {
1433                         BM_select_history_remove(bm, ele);
1434                 }
1435                 BM_elem_select_set(bm, ele, select);
1436         }
1437         BMW_end(&walker);
1438 }
1439
1440 static int edbm_loop_multiselect_exec(bContext *C, wmOperator *op)
1441 {
1442         Object *obedit = CTX_data_edit_object(C);
1443         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1444         BMEdge *eed;
1445         BMEdge **edarray;
1446         int edindex;
1447         const bool is_ring = RNA_boolean_get(op->ptr, "ring");
1448         
1449         BMIter iter;
1450         int totedgesel = 0;
1451
1452         BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1453                 if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1454                         totedgesel++;
1455                 }
1456         }
1457         
1458         edarray = MEM_mallocN(sizeof(BMEdge *) * totedgesel, "edge array");
1459         edindex = 0;
1460         
1461         BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1462                 if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1463                         edarray[edindex] = eed;
1464                         edindex++;
1465                 }
1466         }
1467         
1468         if (is_ring) {
1469                 for (edindex = 0; edindex < totedgesel; edindex += 1) {
1470                         eed = edarray[edindex];
1471                         walker_select(em, BMW_EDGERING, eed, true);
1472                 }
1473                 EDBM_selectmode_flush(em);
1474         }
1475         else {
1476                 for (edindex = 0; edindex < totedgesel; edindex += 1) {
1477                         eed = edarray[edindex];
1478                         walker_select(em, BMW_EDGELOOP, eed, true);
1479                 }
1480                 EDBM_selectmode_flush(em);
1481         }
1482         MEM_freeN(edarray);
1483 //      if (EM_texFaceCheck())
1484         
1485         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1486
1487         return OPERATOR_FINISHED;
1488 }
1489
1490 void MESH_OT_loop_multi_select(wmOperatorType *ot)
1491 {
1492         /* identifiers */
1493         ot->name = "Multi Select Loops";
1494         ot->idname = "MESH_OT_loop_multi_select";
1495         ot->description = "Select a loop of connected edges by connection type";
1496         
1497         /* api callbacks */
1498         ot->exec = edbm_loop_multiselect_exec;
1499         ot->poll = ED_operator_editmesh;
1500         
1501         /* flags */
1502         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1503         
1504         /* properties */
1505         RNA_def_boolean(ot->srna, "ring", 0, "Ring", "");
1506 }
1507
1508                 
1509 /* ***************** MAIN MOUSE SELECTION ************** */
1510
1511
1512 /* ***************** loop select (non modal) ************** */
1513
1514 static void mouse_mesh_loop_face(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
1515 {
1516         if (select_clear) {
1517                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1518         }
1519
1520         walker_select(em, BMW_FACELOOP, eed, select);
1521 }
1522
1523 static void mouse_mesh_loop_edge_ring(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
1524 {
1525         if (select_clear) {
1526                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1527         }
1528
1529         walker_select(em, BMW_EDGERING, eed, select);
1530 }
1531
1532 static void mouse_mesh_loop_edge(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear, bool select_cycle)
1533 {
1534         bool edge_boundary = false;
1535
1536         /* cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY  */
1537         if (select_cycle && BM_edge_is_boundary(eed)) {
1538                 int tot[2];
1539
1540                 /* if the loops selected toggle the boundaries */
1541                 walker_select_count(em, BMW_EDGELOOP, eed, select, false,
1542                                     &tot[0], &tot[1]);
1543                 if (tot[select] == 0) {
1544                         edge_boundary = true;
1545
1546                         /* if the boundaries selected, toggle back to the loop */
1547                         walker_select_count(em, BMW_EDGEBOUNDARY, eed, select, false,
1548                                             &tot[0], &tot[1]);
1549                         if (tot[select] == 0) {
1550                                 edge_boundary = false;
1551                         }
1552                 }
1553         }
1554
1555         if (select_clear) {
1556                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1557         }
1558
1559         if (edge_boundary) {
1560                 walker_select(em, BMW_EDGEBOUNDARY, eed, select);
1561         }
1562         else {
1563                 walker_select(em, BMW_EDGELOOP, eed, select);
1564         }
1565 }
1566
1567
1568 static bool mouse_mesh_loop(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, bool ring)
1569 {
1570         ViewContext vc;
1571         BMEditMesh *em;
1572         BMEdge *eed;
1573         bool select = true;
1574         bool select_clear = false;
1575         bool select_cycle = true;
1576         float dist = ED_view3d_select_dist_px() * 0.6666f;
1577         float mvalf[2];
1578
1579         em_setup_viewcontext(C, &vc);
1580         mvalf[0] = (float)(vc.mval[0] = mval[0]);
1581         mvalf[1] = (float)(vc.mval[1] = mval[1]);
1582         em = vc.em;
1583
1584         /* Make sure that the edges are also considered for selection.
1585          * TODO: cleanup: add `selectmode` as a parameter */
1586         const short ts_selectmode = vc.scene->toolsettings->selectmode;
1587         vc.scene->toolsettings->selectmode |= SCE_SELECT_EDGE;
1588
1589         /* no afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad */
1590         ED_view3d_backbuf_validate(&vc);
1591
1592         /* restore `selectmode` */
1593         vc.scene->toolsettings->selectmode = ts_selectmode;
1594
1595         eed = EDBM_edge_find_nearest_ex(&vc, &dist, NULL, true, true, NULL);
1596         if (eed == NULL) {
1597                 return false;
1598         }
1599
1600         if (extend == false && deselect == false && toggle == false) {
1601                 select_clear = true;
1602         }
1603
1604         if (extend) {
1605                 select = true;
1606         }
1607         else if (deselect) {
1608                 select = false;
1609         }
1610         else if (select_clear || (BM_elem_flag_test(eed, BM_ELEM_SELECT) == 0)) {
1611                 select = true;
1612         }
1613         else if (toggle) {
1614                 select = false;
1615                 select_cycle = false;
1616         }
1617
1618         if (em->selectmode & SCE_SELECT_FACE) {
1619                 mouse_mesh_loop_face(em, eed, select, select_clear);
1620         }
1621         else {
1622                 if (ring) {
1623                         mouse_mesh_loop_edge_ring(em, eed, select, select_clear);
1624                 }
1625                 else {
1626                         mouse_mesh_loop_edge(em, eed, select, select_clear, select_cycle);
1627                 }
1628         }
1629
1630         EDBM_selectmode_flush(em);
1631
1632         /* sets as active, useful for other tools */
1633         if (select) {
1634                 if (em->selectmode & SCE_SELECT_VERTEX) {
1635                         /* Find nearest vert from mouse
1636                          * (initialize to large values incase only one vertex can be projected) */
1637                         float v1_co[2], v2_co[2];
1638                         float length_1 = FLT_MAX;
1639                         float length_2 = FLT_MAX;
1640
1641                         /* We can't be sure this has already been set... */
1642                         ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
1643
1644                         if (ED_view3d_project_float_object(vc.ar, eed->v1->co, v1_co, V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
1645                                 length_1 = len_squared_v2v2(mvalf, v1_co);
1646                         }
1647
1648                         if (ED_view3d_project_float_object(vc.ar, eed->v2->co, v2_co, V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
1649                                 length_2 = len_squared_v2v2(mvalf, v2_co);
1650                         }
1651 #if 0
1652                         printf("mouse to v1: %f\nmouse to v2: %f\n", len_squared_v2v2(mvalf, v1_co),
1653                                len_squared_v2v2(mvalf, v2_co));
1654 #endif
1655                         BM_select_history_store(em->bm, (length_1 < length_2) ? eed->v1 : eed->v2);
1656                 }
1657                 else if (em->selectmode & SCE_SELECT_EDGE) {
1658                         BM_select_history_store(em->bm, eed);
1659                 }
1660                 else if (em->selectmode & SCE_SELECT_FACE) {
1661                         /* Select the face of eed which is the nearest of mouse. */
1662                         BMFace *f, *efa = NULL;
1663                         BMIter iterf;
1664                         float best_dist = FLT_MAX;
1665
1666                         /* We can't be sure this has already been set... */
1667                         ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
1668
1669                         BM_ITER_ELEM (f, &iterf, eed, BM_FACES_OF_EDGE) {
1670                                 if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
1671                                         float cent[3];
1672                                         float co[2], tdist;
1673
1674                                         BM_face_calc_center_mean(f, cent);
1675                                         if (ED_view3d_project_float_object(vc.ar, cent, co, V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
1676                                                 tdist = len_squared_v2v2(mvalf, co);
1677                                                 if (tdist < best_dist) {
1678 /*                                                      printf("Best face: %p (%f)\n", f, tdist);*/
1679                                                         best_dist = tdist;
1680                                                         efa = f;
1681                                                 }
1682                                         }
1683                                 }
1684                         }
1685                         if (efa) {
1686                                 BM_mesh_active_face_set(em->bm, efa);
1687                                 BM_select_history_store(em->bm, efa);
1688                         }
1689                 }
1690         }
1691
1692         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
1693
1694         return true;
1695 }
1696
1697 static int edbm_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1698 {
1699         
1700         view3d_operator_needs_opengl(C);
1701         
1702         if (mouse_mesh_loop(C, event->mval,
1703                             RNA_boolean_get(op->ptr, "extend"),
1704                             RNA_boolean_get(op->ptr, "deselect"),
1705                             RNA_boolean_get(op->ptr, "toggle"),
1706                             RNA_boolean_get(op->ptr, "ring")))
1707         {
1708                 return OPERATOR_FINISHED;
1709         }
1710         else {
1711                 return OPERATOR_CANCELLED;
1712         }
1713 }
1714
1715 void MESH_OT_loop_select(wmOperatorType *ot)
1716 {
1717         /* identifiers */
1718         ot->name = "Loop Select";
1719         ot->idname = "MESH_OT_loop_select";
1720         ot->description = "Select a loop of connected edges";
1721         
1722         /* api callbacks */
1723         ot->invoke = edbm_select_loop_invoke;
1724         ot->poll = ED_operator_editmesh_region_view3d;
1725         
1726         /* flags */
1727         ot->flag = OPTYPE_UNDO;
1728         
1729         /* properties */
1730         RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection");
1731         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection");
1732         RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection");
1733         RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring");
1734 }
1735
1736 void MESH_OT_edgering_select(wmOperatorType *ot)
1737 {
1738         /* description */
1739         ot->name = "Edge Ring Select";
1740         ot->idname = "MESH_OT_edgering_select";
1741         ot->description = "Select an edge ring";
1742         
1743         /* callbacks */
1744         ot->invoke = edbm_select_loop_invoke;
1745         ot->poll = ED_operator_editmesh_region_view3d;
1746         
1747         /* flags */
1748         ot->flag = OPTYPE_UNDO;
1749
1750         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
1751         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection");
1752         RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection");
1753         RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring");
1754 }
1755
1756 /* ******************** (de)select all operator **************** */
1757 static int edbm_select_all_exec(bContext *C, wmOperator *op)
1758 {
1759         Object *obedit = CTX_data_edit_object(C);
1760         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1761         const int action = RNA_enum_get(op->ptr, "action");
1762
1763         switch (action) {
1764                 case SEL_TOGGLE:
1765                         EDBM_select_toggle_all(em);
1766                         break;
1767                 case SEL_SELECT:
1768                         EDBM_flag_enable_all(em, BM_ELEM_SELECT);
1769                         break;
1770                 case SEL_DESELECT:
1771                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1772                         break;
1773                 case SEL_INVERT:
1774                         EDBM_select_swap(em);
1775                         EDBM_selectmode_flush(em);
1776                         break;
1777         }
1778
1779         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1780
1781         return OPERATOR_FINISHED;
1782 }
1783
1784 void MESH_OT_select_all(wmOperatorType *ot)
1785 {
1786         /* identifiers */
1787         ot->name = "(De)select All";
1788         ot->idname = "MESH_OT_select_all";
1789         ot->description = "(De)select all vertices, edges or faces";
1790
1791         /* api callbacks */
1792         ot->exec = edbm_select_all_exec;
1793         ot->poll = ED_operator_editmesh;
1794
1795         /* flags */
1796         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1797
1798         WM_operator_properties_select_all(ot);
1799 }
1800
1801 static int edbm_faces_select_interior_exec(bContext *C, wmOperator *UNUSED(op))
1802 {
1803         Object *obedit = CTX_data_edit_object(C);
1804         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1805
1806         if (EDBM_select_interior_faces(em)) {
1807                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1808
1809                 return OPERATOR_FINISHED;
1810         }
1811         else {
1812                 return OPERATOR_CANCELLED;
1813         }
1814
1815 }
1816
1817 void MESH_OT_select_interior_faces(wmOperatorType *ot)
1818 {
1819         /* identifiers */
1820         ot->name = "Select Interior Faces";
1821         ot->idname = "MESH_OT_select_interior_faces";
1822         ot->description = "Select faces where all edges have more than 2 face users";
1823
1824         /* api callbacks */
1825         ot->exec = edbm_faces_select_interior_exec;
1826         ot->poll = ED_operator_editmesh;
1827
1828         /* flags */
1829         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1830 }
1831
1832
1833 /* ************************************************** */
1834 /* here actual select happens */
1835 /* gets called via generic mouse select operator */
1836 bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
1837 {
1838         ViewContext vc;
1839         BMVert *eve = NULL;
1840         BMEdge *eed = NULL;
1841         BMFace *efa = NULL;
1842
1843         /* setup view context for argument to callbacks */
1844         em_setup_viewcontext(C, &vc);
1845         vc.mval[0] = mval[0];
1846         vc.mval[1] = mval[1];
1847
1848         if (unified_findnearest(&vc, &eve, &eed, &efa)) {
1849
1850                 /* Deselect everything */
1851                 if (extend == false && deselect == false && toggle == false)
1852                         EDBM_flag_disable_all(vc.em, BM_ELEM_SELECT);
1853
1854                 if (efa) {
1855                         if (extend) {
1856                                 /* set the last selected face */
1857                                 BM_mesh_active_face_set(vc.em->bm, efa);
1858
1859                                 /* Work-around: deselect first, so we can guarantee it will */
1860                                 /* be active even if it was already selected */
1861                                 BM_select_history_remove(vc.em->bm, efa);
1862                                 BM_face_select_set(vc.em->bm, efa, false);
1863                                 BM_select_history_store(vc.em->bm, efa);
1864                                 BM_face_select_set(vc.em->bm, efa, true);
1865                         }
1866                         else if (deselect) {
1867                                 BM_select_history_remove(vc.em->bm, efa);
1868                                 BM_face_select_set(vc.em->bm, efa, false);
1869                         }
1870                         else {
1871                                 /* set the last selected face */
1872                                 BM_mesh_active_face_set(vc.em->bm, efa);
1873
1874                                 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1875                                         BM_select_history_store(vc.em->bm, efa);
1876                                         BM_face_select_set(vc.em->bm, efa, true);
1877                                 }
1878                                 else if (toggle) {
1879                                         BM_select_history_remove(vc.em->bm, efa);
1880                                         BM_face_select_set(vc.em->bm, efa, false);
1881                                 }
1882                         }
1883                 }
1884                 else if (eed) {
1885                         if (extend) {
1886                                 /* Work-around: deselect first, so we can guarantee it will */
1887                                 /* be active even if it was already selected */
1888                                 BM_select_history_remove(vc.em->bm, eed);
1889                                 BM_edge_select_set(vc.em->bm, eed, false);
1890                                 BM_select_history_store(vc.em->bm, eed);
1891                                 BM_edge_select_set(vc.em->bm, eed, true);
1892                         }
1893                         else if (deselect) {
1894                                 BM_select_history_remove(vc.em->bm, eed);
1895                                 BM_edge_select_set(vc.em->bm, eed, false);
1896                         }
1897                         else {
1898                                 if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1899                                         BM_select_history_store(vc.em->bm, eed);
1900                                         BM_edge_select_set(vc.em->bm, eed, true);
1901                                 }
1902                                 else if (toggle) {
1903                                         BM_select_history_remove(vc.em->bm, eed);
1904                                         BM_edge_select_set(vc.em->bm, eed, false);
1905                                 }
1906                         }
1907                 }
1908                 else if (eve) {
1909                         if (extend) {
1910                                 /* Work-around: deselect first, so we can guarantee it will */
1911                                 /* be active even if it was already selected */
1912                                 BM_select_history_remove(vc.em->bm, eve);
1913                                 BM_vert_select_set(vc.em->bm, eve, false);
1914                                 BM_select_history_store(vc.em->bm, eve);
1915                                 BM_vert_select_set(vc.em->bm, eve, true);
1916                         }
1917                         else if (deselect) {
1918                                 BM_select_history_remove(vc.em->bm, eve);
1919                                 BM_vert_select_set(vc.em->bm, eve, false);
1920                         }
1921                         else {
1922                                 if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
1923                                         BM_select_history_store(vc.em->bm, eve);
1924                                         BM_vert_select_set(vc.em->bm, eve, true);
1925                                 }
1926                                 else if (toggle) {
1927                                         BM_select_history_remove(vc.em->bm, eve);
1928                                         BM_vert_select_set(vc.em->bm, eve, false);
1929                                 }
1930                         }
1931                 }
1932
1933                 EDBM_selectmode_flush(vc.em);
1934
1935                 /* change active material on object */
1936                 if (efa && efa->mat_nr != vc.obedit->actcol - 1) {
1937                         vc.obedit->actcol = efa->mat_nr + 1;
1938                         vc.em->mat_nr = efa->mat_nr;
1939
1940                         WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, NULL);
1941
1942                 }
1943
1944                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
1945                 return true;
1946         }
1947
1948         return false;
1949 }
1950
1951 static void edbm_strip_selections(BMEditMesh *em)
1952 {
1953         BMEditSelection *ese, *nextese;
1954
1955         if (!(em->selectmode & SCE_SELECT_VERTEX)) {
1956                 ese = em->bm->selected.first;
1957                 while (ese) {
1958                         nextese = ese->next;
1959                         if (ese->htype == BM_VERT) BLI_freelinkN(&(em->bm->selected), ese);
1960                         ese = nextese;
1961                 }
1962         }
1963         if (!(em->selectmode & SCE_SELECT_EDGE)) {
1964                 ese = em->bm->selected.first;
1965                 while (ese) {
1966                         nextese = ese->next;
1967                         if (ese->htype == BM_EDGE) BLI_freelinkN(&(em->bm->selected), ese);
1968                         ese = nextese;
1969                 }
1970         }
1971         if (!(em->selectmode & SCE_SELECT_FACE)) {
1972                 ese = em->bm->selected.first;
1973                 while (ese) {
1974                         nextese = ese->next;
1975                         if (ese->htype == BM_FACE) BLI_freelinkN(&(em->bm->selected), ese);
1976                         ese = nextese;
1977                 }
1978         }
1979 }
1980
1981 /* when switching select mode, makes sure selection is consistent for editing */
1982 /* also for paranoia checks to make sure edge or face mode works */
1983 void EDBM_selectmode_set(BMEditMesh *em)
1984 {
1985         BMVert *eve;
1986         BMEdge *eed;
1987         BMFace *efa;
1988         BMIter iter;
1989         
1990         em->bm->selectmode = em->selectmode;
1991
1992         edbm_strip_selections(em); /* strip BMEditSelections from em->selected that are not relevant to new mode */
1993         
1994         if (em->bm->totvertsel == 0 &&
1995             em->bm->totedgesel == 0 &&
1996             em->bm->totfacesel == 0)
1997         {
1998                 return;
1999         }
2000
2001         if (em->selectmode & SCE_SELECT_VERTEX) {
2002                 if (em->bm->totvertsel) {
2003                         EDBM_select_flush(em);
2004                 }
2005         }
2006         else if (em->selectmode & SCE_SELECT_EDGE) {
2007                 /* deselect vertices, and select again based on edge select */
2008                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2009                         BM_vert_select_set(em->bm, eve, false);
2010                 }
2011
2012                 if (em->bm->totedgesel) {
2013                         BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2014                                 if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
2015                                         BM_edge_select_set(em->bm, eed, true);
2016                                 }
2017                         }
2018
2019                         /* selects faces based on edge status */
2020                         EDBM_selectmode_flush(em);
2021                 }
2022         }
2023         else if (em->selectmode & SCE_SELECT_FACE) {
2024                 /* deselect eges, and select again based on face select */
2025                 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2026                         BM_edge_select_set(em->bm, eed, false);
2027                 }
2028
2029                 if (em->bm->totfacesel) {
2030                         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2031                                 if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2032                                         BM_face_select_set(em->bm, efa, true);
2033                                 }
2034                         }
2035                 }
2036         }
2037 }
2038
2039 /**
2040  * Expand & Contract the Selection
2041  * (used when changing modes and Ctrl key held)
2042  *
2043  * Flush the selection up:
2044  * - vert -> edge
2045  * - vert -> face
2046  * - edge -> face
2047  *
2048  * Flush the selection down:
2049  * - face -> edge
2050  * - face -> vert
2051  * - edge -> vert
2052  */
2053 void EDBM_selectmode_convert(BMEditMesh *em, const short selectmode_old, const short selectmode_new)
2054 {
2055         BMesh *bm = em->bm;
2056
2057         BMVert *eve;
2058         BMEdge *eed;
2059         BMFace *efa;
2060         BMIter iter;
2061
2062         /* first tag-to-select, then select --- this avoids a feedback loop */
2063
2064         /* have to find out what the selectionmode was previously */
2065         if (selectmode_old == SCE_SELECT_VERTEX) {
2066                 if (bm->totvertsel == 0) {
2067                         /* pass */
2068                 }
2069                 else if (selectmode_new == SCE_SELECT_EDGE) {
2070                         /* flush up (vert -> edge) */
2071
2072                         /* select all edges associated with every selected vert */
2073                         BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2074                                 BM_elem_flag_set(eed, BM_ELEM_TAG, BM_edge_is_any_vert_flag_test(eed, BM_ELEM_SELECT));
2075                         }
2076
2077                         BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2078                                 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
2079                                         BM_edge_select_set(bm, eed, true);
2080                                 }
2081                         }
2082                 }
2083                 else if (selectmode_new == SCE_SELECT_FACE) {
2084                         /* flush up (vert -> face) */
2085
2086                         /* select all faces associated with every selected vert */
2087                         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2088                                 BM_elem_flag_set(efa, BM_ELEM_TAG, BM_face_is_any_vert_flag_test(efa, BM_ELEM_SELECT));
2089                         }
2090
2091                         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2092                                 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2093                                         BM_face_select_set(bm, efa, true);
2094                                 }
2095                         }
2096                 }
2097         }
2098         else if (selectmode_old == SCE_SELECT_EDGE) {
2099                 if (bm->totedgesel == 0) {
2100                         /* pass */
2101                 }
2102                 else if (selectmode_new == SCE_SELECT_FACE) {
2103                         /* flush up (edge -> face) */
2104
2105                         /* select all faces associated with every selected edge */
2106                         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2107                                 BM_elem_flag_set(efa, BM_ELEM_TAG, BM_face_is_any_edge_flag_test(efa, BM_ELEM_SELECT));
2108                         }
2109
2110                         BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2111                                 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2112                                         BM_face_select_set(bm, efa, true);
2113                                 }
2114                         }
2115                 }
2116                 else if (selectmode_new == SCE_SELECT_VERTEX) {
2117                         /* flush down (edge -> vert) */
2118
2119                         BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2120                                 if (!BM_vert_is_all_edge_flag_test(eve, BM_ELEM_SELECT, true)) {
2121                                         BM_vert_select_set(bm, eve, false);
2122                                 }
2123                         }
2124                         /* deselect edges without both verts selected */
2125                         BM_mesh_deselect_flush(bm);
2126                 }
2127         }
2128         else if (selectmode_old == SCE_SELECT_FACE) {
2129                 if (bm->totfacesel == 0) {
2130                         /* pass */
2131                 }
2132                 else if (selectmode_new == SCE_SELECT_EDGE) {
2133                         /* flush down (face -> edge) */
2134
2135                         BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2136                                 if (!BM_edge_is_all_face_flag_test(eed, BM_ELEM_SELECT, true)) {
2137                                         BM_edge_select_set(bm, eed, false);
2138                                 }
2139                         }
2140                 }
2141                 else if (selectmode_new == SCE_SELECT_VERTEX) {
2142                         /* flush down (face -> vert) */
2143
2144                         BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2145                                 if (!BM_vert_is_all_face_flag_test(eve, BM_ELEM_SELECT, true)) {
2146                                         BM_vert_select_set(bm, eve, false);
2147                                 }
2148                         }
2149                         /* deselect faces without verts selected */
2150                         BM_mesh_deselect_flush(bm);
2151                 }
2152         }
2153 }
2154
2155 /* user facing function, does notification */
2156 bool EDBM_selectmode_toggle(bContext *C, const short selectmode_new,
2157                             const int action, const bool use_extend, const bool use_expand)
2158 {
2159         ToolSettings *ts = CTX_data_tool_settings(C);
2160         Object *obedit = CTX_data_edit_object(C);
2161         BMEditMesh *em = NULL;
2162         bool ret = false;
2163
2164         if (obedit && obedit->type == OB_MESH) {
2165                 em = BKE_editmesh_from_object(obedit);
2166         }
2167
2168         if (em == NULL) {
2169                 return ret;
2170         }
2171
2172         switch (action) {
2173                 case -1:
2174                         /* already set */
2175                         break;
2176                 case 0:  /* disable */
2177                         /* check we have something to do */
2178                         if ((em->selectmode & selectmode_new) == 0) {
2179                                 return false;
2180                         }
2181                         em->selectmode &= ~selectmode_new;
2182                         break;
2183                 case 1:  /* enable */
2184                         /* check we have something to do */
2185                         if ((em->selectmode & selectmode_new) != 0) {
2186                                 return false;
2187                         }
2188                         em->selectmode |= selectmode_new;
2189                         break;
2190                 case 2:  /* toggle */
2191                         /* can't disable this flag if its the only one set */
2192                         if (em->selectmode == selectmode_new) {
2193                                 return false;
2194                         }
2195                         em->selectmode ^= selectmode_new;
2196                         break;
2197                 default:
2198                         BLI_assert(0);
2199                         break;
2200         }
2201
2202         if (use_extend == 0 || em->selectmode == 0) {
2203                 if (use_expand) {
2204                         const short selmode_max = highest_order_bit_s(ts->selectmode);
2205                         EDBM_selectmode_convert(em, selmode_max, selectmode_new);
2206                 }
2207         }
2208
2209         switch (selectmode_new) {
2210                 case SCE_SELECT_VERTEX:
2211                         if (use_extend == 0 || em->selectmode == 0) {
2212                                 em->selectmode = SCE_SELECT_VERTEX;
2213                         }
2214                         ts->selectmode = em->selectmode;
2215                         EDBM_selectmode_set(em);
2216                         ret = true;
2217                         break;
2218                 case SCE_SELECT_EDGE:
2219                         if (use_extend == 0 || em->selectmode == 0) {
2220                                 em->selectmode = SCE_SELECT_EDGE;
2221                         }
2222                         ts->selectmode = em->selectmode;
2223                         EDBM_selectmode_set(em);
2224                         ret = true;
2225                         break;
2226                 case SCE_SELECT_FACE:
2227                         if (use_extend == 0 || em->selectmode == 0) {
2228                                 em->selectmode = SCE_SELECT_FACE;
2229                         }
2230                         ts->selectmode = em->selectmode;
2231                         EDBM_selectmode_set(em);
2232                         ret = true;
2233                         break;
2234                 default:
2235                         BLI_assert(0);
2236                         break;
2237         }
2238
2239         if (ret == true) {
2240                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2241                 WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
2242         }
2243
2244         return ret;
2245 }
2246
2247 /**
2248  * Use to disable a selectmode if its enabled, Using another mode as a fallback
2249  * if the disabled mode is the only mode set.
2250  *
2251  * \return true if the mode is changed.
2252  */
2253 bool EDBM_selectmode_disable(Scene *scene, BMEditMesh *em,
2254                              const short selectmode_disable,
2255                              const short selectmode_fallback)
2256 {
2257         /* note essential, but switch out of vertex mode since the
2258          * selected regions wont be nicely isolated after flushing */
2259         if (em->selectmode & selectmode_disable) {
2260                 if (em->selectmode == selectmode_disable) {
2261                         em->selectmode = selectmode_fallback;
2262                 }
2263                 else {
2264                         em->selectmode &= ~selectmode_disable;
2265                 }
2266                 scene->toolsettings->selectmode = em->selectmode;
2267                 EDBM_selectmode_set(em);
2268
2269                 WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, scene);
2270
2271                 return true;
2272         }
2273         else {
2274                 return false;
2275         }
2276 }
2277
2278 void EDBM_deselect_by_material(BMEditMesh *em, const short index, const bool select)
2279 {
2280         BMIter iter;
2281         BMFace *efa;
2282
2283         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2284                 if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
2285                         continue;
2286                 if (efa->mat_nr == index) {
2287                         BM_face_select_set(em->bm, efa, select);
2288                 }
2289         }
2290 }
2291
2292 void EDBM_select_toggle_all(BMEditMesh *em) /* exported for UV */
2293 {
2294         if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel)
2295                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
2296         else
2297                 EDBM_flag_enable_all(em, BM_ELEM_SELECT);
2298 }
2299
2300 void EDBM_select_swap(BMEditMesh *em) /* exported for UV */
2301 {
2302         BMIter iter;
2303         BMVert *eve;
2304         BMEdge *eed;
2305         BMFace *efa;
2306         
2307         if (em->bm->selectmode & SCE_SELECT_VERTEX) {
2308                 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2309                         if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN))
2310                                 continue;
2311                         BM_vert_select_set(em->bm, eve, !BM_elem_flag_test(eve, BM_ELEM_SELECT));
2312                 }
2313         }
2314         else if (em->selectmode & SCE_SELECT_EDGE) {
2315                 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2316                         if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
2317                                 continue;
2318                         BM_edge_select_set(em->bm, eed, !BM_elem_flag_test(eed, BM_ELEM_SELECT));
2319                 }
2320         }
2321         else {
2322                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2323                         if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
2324                                 continue;
2325                         BM_face_select_set(em->bm, efa, !BM_elem_flag_test(efa, BM_ELEM_SELECT));
2326                 }
2327
2328         }
2329 //      if (EM_texFaceCheck())
2330 }
2331
2332 bool EDBM_select_interior_faces(BMEditMesh *em)
2333 {
2334         BMesh *bm = em->bm;
2335         BMIter iter;
2336         BMIter eiter;
2337         BMFace *efa;
2338         BMEdge *eed;
2339         bool ok;
2340         bool changed = false;
2341
2342         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2343                 if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN))
2344                         continue;
2345
2346
2347                 ok = true;
2348                 BM_ITER_ELEM (eed, &eiter, efa, BM_EDGES_OF_FACE) {
2349                         if (!BM_edge_face_count_is_over(eed, 2)) {
2350                                 ok = false;
2351                                 break;
2352                         }
2353                 }
2354
2355                 if (ok) {
2356                         BM_face_select_set(bm, efa, true);
2357                         changed = true;
2358                 }
2359         }
2360
2361         return changed;
2362 }
2363
2364
2365 /************************ Select Linked Operator *************************/
2366
2367 /* so we can have last-used default depend on selection mode (rare exception!) */
2368 #define USE_LINKED_SELECT_DEFAULT_HACK
2369
2370 struct DelimitData {
2371         int cd_loop_type;
2372         int cd_loop_offset;
2373 };
2374
2375 static bool select_linked_delimit_test(
2376         BMEdge *e, int delimit,
2377         const struct DelimitData *delimit_data)
2378 {
2379         BLI_assert(delimit);
2380
2381         if (delimit & BMO_DELIM_SEAM) {
2382                 if (BM_elem_flag_test(e, BM_ELEM_SEAM)) {
2383                         return true;
2384                 }
2385         }
2386
2387         if (delimit & BMO_DELIM_SHARP) {
2388                 if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0) {
2389                         return true;
2390                 }
2391         }
2392
2393         if (delimit & BMO_DELIM_NORMAL) {
2394                 if (!BM_edge_is_contiguous(e)) {
2395                         return true;
2396                 }
2397         }
2398
2399         if (delimit & BMO_DELIM_MATERIAL) {
2400                 if (e->l && e->l->radial_next != e->l) {
2401                         const short mat_nr = e->l->f->mat_nr;
2402                         BMLoop *l_iter = e->l->radial_next;
2403                         do {
2404                                 if (l_iter->f->mat_nr != mat_nr) {
2405                                         return true;
2406                                 }
2407                         } while ((l_iter = l_iter->radial_next) != e->l);
2408                 }
2409         }
2410
2411         if (delimit & BMO_DELIM_UV) {
2412                 if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_loop_type, delimit_data->cd_loop_offset) == 0) {
2413                         return true;
2414                 }
2415         }
2416
2417         return false;
2418 }
2419
2420 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2421 /**
2422  * Gets the default from the operator fallback to own last-used value
2423  * (selected based on mode)
2424  */
2425 static int select_linked_delimit_default_from_op(wmOperator *op, BMEditMesh *em)
2426 {
2427         static char delimit_last_store[2] = {0, BMO_DELIM_SEAM};
2428         int delimit_last_index = (em->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0;
2429         char *delimit_last = &delimit_last_store[delimit_last_index];
2430         PropertyRNA *prop_delimit = RNA_struct_find_property(op->ptr, "delimit");
2431         int delimit;
2432
2433         if (RNA_property_is_set(op->ptr, prop_delimit)) {
2434                 delimit = RNA_property_enum_get(op->ptr, prop_delimit);
2435                 *delimit_last = delimit;
2436         }
2437         else {
2438                 delimit = *delimit_last;
2439                 RNA_property_enum_set(op->ptr, prop_delimit, delimit);
2440         }
2441         return delimit;
2442 }
2443 #endif
2444
2445 static void select_linked_delimit_validate(BMesh *bm, int *delimit)
2446 {
2447         if ((*delimit) & BMO_DELIM_UV) {
2448                 if (!CustomData_has_layer(&bm->ldata, CD_MLOOPUV)) {
2449                         (*delimit) &= ~BMO_DELIM_UV;
2450                 }
2451         }
2452 }
2453
2454 static void select_linked_delimit_begin(BMesh *bm, int delimit)
2455 {
2456         struct DelimitData delimit_data = {0};
2457
2458         if (delimit & BMO_DELIM_UV) {
2459                 delimit_data.cd_loop_type = CD_MLOOPUV;
2460                 delimit_data.cd_loop_offset = CustomData_get_offset(&bm->ldata, delimit_data.cd_loop_type);
2461                 if (delimit_data.cd_loop_offset == -1) {
2462                         delimit &= ~BMO_DELIM_UV;
2463                 }
2464         }
2465
2466         /* grr, shouldn't need to alloc BMO flags here */
2467         BM_mesh_elem_toolflags_ensure(bm);
2468
2469         {
2470                 BMIter iter;
2471                 BMEdge *e;
2472
2473                 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
2474                         const bool is_walk_ok = (
2475                                 (select_linked_delimit_test(e, delimit, &delimit_data) == false));
2476
2477                         BMO_edge_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok);
2478                 }
2479         }
2480 }
2481
2482 static void select_linked_delimit_end(BMEditMesh *em)
2483 {
2484         BMesh *bm = em->bm;
2485
2486         BM_mesh_elem_toolflags_clear(bm);
2487 }
2488
2489 static int edbm_select_linked_exec(bContext *C, wmOperator *op)
2490 {
2491         Object *obedit = CTX_data_edit_object(C);
2492         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2493         BMesh *bm = em->bm;
2494         BMIter iter;
2495         BMWalker walker;
2496
2497 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2498         int delimit = select_linked_delimit_default_from_op(op, em);
2499 #else
2500         int delimit = RNA_enum_get(op->ptr, "delimit");
2501 #endif
2502
2503         select_linked_delimit_validate(bm, &delimit);
2504
2505         if (delimit) {
2506                 select_linked_delimit_begin(em->bm, delimit);
2507         }
2508
2509         if (em->selectmode & SCE_SELECT_VERTEX) {
2510                 BMVert *v;
2511
2512                 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
2513                         BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT));
2514                 }
2515
2516                 /* exclude all delimited verts */
2517                 if (delimit) {
2518                         BMEdge *e;
2519                         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
2520                                 if (!BMO_edge_flag_test(bm, e, BMO_ELE_TAG)) {
2521                                         BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
2522                                         BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
2523                                 }
2524                         }
2525                 }
2526
2527                 BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
2528                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2529                          BMW_FLAG_TEST_HIDDEN,
2530                          BMW_NIL_LAY);
2531
2532                 if (delimit) {
2533                         BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
2534                                 if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
2535                                         BMElem *ele_walk;
2536                                         BMW_ITER (ele_walk, &walker, v) {
2537                                                 if (ele_walk->head.htype == BM_LOOP) {
2538                                                         BMVert *v_step = ((BMLoop *)ele_walk)->v;
2539                                                         BM_vert_select_set(em->bm, v_step, true);
2540                                                         BM_elem_flag_disable(v_step, BM_ELEM_TAG);
2541                                                 }
2542                                                 else {
2543                                                         BMEdge *e_step = (BMEdge *)ele_walk;
2544                                                         BLI_assert(ele_walk->head.htype == BM_EDGE);
2545                                                         BM_edge_select_set(em->bm, e_step, true);
2546                                                         BM_elem_flag_disable(e_step->v1, BM_ELEM_TAG);
2547                                                         BM_elem_flag_disable(e_step->v2, BM_ELEM_TAG);
2548                                                 }
2549                                         }
2550                                 }
2551                         }
2552                 }
2553                 else {
2554                         BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
2555                                 if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
2556                                         BMEdge *e_walk;
2557                                         BMW_ITER (e_walk, &walker, v) {
2558                                                 BM_edge_select_set(em->bm, e_walk, true);
2559                                                 BM_elem_flag_disable(e_walk, BM_ELEM_TAG);
2560                                         }
2561                                 }
2562                         }
2563                 }
2564
2565                 BMW_end(&walker);
2566
2567                 EDBM_selectmode_flush(em);
2568         }
2569         else if (em->selectmode & SCE_SELECT_EDGE) {
2570                 BMEdge *e;
2571
2572                 if (delimit) {
2573                         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
2574                                 BM_elem_flag_set(
2575                                         e, BM_ELEM_TAG,
2576                                         (BM_elem_flag_test(e, BM_ELEM_SELECT) && BMO_edge_flag_test(bm, e, BMO_ELE_TAG)));
2577                         }
2578                 }
2579                 else {
2580                         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
2581                                 BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT));
2582                         }
2583                 }
2584
2585                 BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
2586                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2587                          BMW_FLAG_TEST_HIDDEN,
2588                          BMW_NIL_LAY);
2589
2590                 if (delimit) {
2591                         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
2592                                 if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
2593                                         BMElem *ele_walk;
2594                                         BMW_ITER (ele_walk, &walker, e) {
2595                                                 if (ele_walk->head.htype == BM_LOOP) {
2596                                                         BMLoop *l_step = (BMLoop *)ele_walk;
2597                                                         BM_edge_select_set(em->bm, l_step->e, true);
2598                                                         BM_edge_select_set(em->bm, l_step->prev->e, true);
2599                                                         BM_elem_flag_disable(l_step->e, BM_ELEM_TAG);
2600                                                 }
2601                                                 else {
2602                                                         BMEdge *e_step = (BMEdge *)ele_walk;
2603                                                         BLI_assert(ele_walk->head.htype == BM_EDGE);
2604                                                         BM_edge_select_set(em->bm, e_step, true);
2605                                                         BM_elem_flag_disable(e_step, BM_ELEM_TAG);
2606                                                 }
2607                                         }
2608                                 }
2609                         }
2610                 }
2611                 else {
2612                         BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
2613                                 if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
2614                                         BMEdge *e_walk;
2615                                         BMW_ITER (e_walk, &walker, e) {
2616                                                 BM_edge_select_set(em->bm, e_walk, true);
2617                                                 BM_elem_flag_disable(e_walk, BM_ELEM_TAG);
2618                                         }
2619                                 }
2620                         }
2621                 }
2622
2623                 BMW_end(&walker);
2624
2625                 EDBM_selectmode_flush(em);
2626         }
2627         else {
2628                 BMFace *f;
2629
2630                 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
2631                         BM_elem_flag_set(f, BM_ELEM_TAG, BM_elem_flag_test(f, BM_ELEM_SELECT));
2632                 }
2633
2634                 BMW_init(&walker, bm, BMW_ISLAND,
2635                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2636                          BMW_FLAG_TEST_HIDDEN,
2637                          BMW_NIL_LAY);
2638
2639                 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
2640                         if (BM_elem_flag_test(f, BM_ELEM_TAG)) {
2641                                 BMFace *f_walk;
2642                                 BMW_ITER (f_walk, &walker, f) {
2643                                         BM_face_select_set(bm, f_walk, true);
2644                                         BM_elem_flag_disable(f_walk, BM_ELEM_TAG);
2645                                 }
2646                         }
2647                 }
2648
2649                 BMW_end(&walker);
2650         }
2651
2652         if (delimit) {
2653                 select_linked_delimit_end(em);
2654         }
2655
2656         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2657
2658         return OPERATOR_FINISHED;
2659 }
2660
2661 void MESH_OT_select_linked(wmOperatorType *ot)
2662 {
2663         PropertyRNA *prop;
2664
2665         /* identifiers */
2666         ot->name = "Select Linked All";
2667         ot->idname = "MESH_OT_select_linked";
2668         ot->description = "Select all vertices connected to the current selection";
2669
2670         /* api callbacks */
2671         ot->exec = edbm_select_linked_exec;
2672         ot->poll = ED_operator_editmesh;
2673
2674         /* flags */
2675         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2676
2677         prop = RNA_def_enum_flag(ot->srna, "delimit", rna_enum_mesh_delimit_mode_items, BMO_DELIM_SEAM, "Delimit",
2678                                  "Delimit selected region");
2679 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2680         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2681 #else
2682         UNUSED_VARS(prop);
2683 #endif
2684 }
2685
2686 static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op);
2687
2688 static void edbm_select_linked_pick_ex(BMEditMesh *em, BMElem *ele, bool sel, int delimit)
2689 {
2690         BMesh *bm = em->bm;
2691         BMWalker walker;
2692
2693         select_linked_delimit_validate(bm, &delimit);
2694
2695         if (delimit) {
2696                 select_linked_delimit_begin(bm, delimit);
2697         }
2698
2699         /* Note: logic closely matches 'edbm_select_linked_exec', keep in sync */
2700
2701         if (ele->head.htype == BM_VERT) {
2702                 BMVert *eve = (BMVert *)ele;
2703
2704                 BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
2705                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2706                          BMW_FLAG_TEST_HIDDEN,
2707                          BMW_NIL_LAY);
2708
2709                 if (delimit) {
2710                         BMElem *ele_walk;
2711                         BMW_ITER (ele_walk, &walker, eve) {
2712                                 if (ele_walk->head.htype == BM_LOOP) {
2713                                         BMVert *v_step = ((BMLoop *)ele_walk)->v;
2714                                         BM_vert_select_set(bm, v_step, sel);
2715                                 }
2716                                 else {
2717                                         BMEdge *e_step = (BMEdge *)ele_walk;
2718                                         BLI_assert(ele_walk->head.htype == BM_EDGE);
2719                                         BM_edge_select_set(bm, e_step, sel);
2720                                 }
2721                         }
2722                 }
2723                 else {
2724                         BMEdge *e_walk;
2725                         BMW_ITER (e_walk, &walker, eve) {
2726                                 BM_edge_select_set(bm, e_walk, sel);
2727                         }
2728                 }
2729
2730                 BMW_end(&walker);
2731
2732                 EDBM_selectmode_flush(em);
2733         }
2734         else if (ele->head.htype == BM_EDGE) {
2735                 BMEdge *eed = (BMEdge *)ele;
2736
2737                 BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
2738                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2739                          BMW_FLAG_TEST_HIDDEN,
2740                          BMW_NIL_LAY);
2741
2742                 if (delimit) {
2743                         BMElem *ele_walk;
2744                         BMW_ITER (ele_walk, &walker, eed) {
2745                                 if (ele_walk->head.htype == BM_LOOP) {
2746                                         BMEdge *e_step = ((BMLoop *)ele_walk)->e;
2747                                         BM_edge_select_set(bm, e_step, sel);
2748                                 }
2749                                 else {
2750                                         BMEdge *e_step = (BMEdge *)ele_walk;
2751                                         BLI_assert(ele_walk->head.htype == BM_EDGE);
2752                                         BM_edge_select_set(bm, e_step, sel);
2753                                 }
2754                         }
2755                 }
2756                 else {
2757                         BMEdge *e_walk;
2758                         BMW_ITER (e_walk, &walker, eed) {
2759                                 BM_edge_select_set(bm, e_walk, sel);
2760                         }
2761                 }
2762
2763                 BMW_end(&walker);
2764
2765                 EDBM_selectmode_flush(em);
2766         }
2767         else if (ele->head.htype == BM_FACE) {
2768                 BMFace *efa = (BMFace *)ele;
2769
2770                 BMW_init(&walker, bm, BMW_ISLAND,
2771                          BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
2772                          BMW_FLAG_TEST_HIDDEN,
2773                          BMW_NIL_LAY);
2774
2775                 {
2776                         BMFace *f_walk;
2777                         BMW_ITER (f_walk, &walker, efa) {
2778                                 BM_face_select_set(bm, f_walk, sel);
2779                                 BM_elem_flag_disable(f_walk, BM_ELEM_TAG);
2780                         }
2781                 }
2782
2783                 BMW_end(&walker);
2784         }
2785
2786         if (delimit) {
2787                 select_linked_delimit_end(em);
2788         }
2789 }
2790
2791 static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2792 {
2793         Object *obedit = CTX_data_edit_object(C);
2794         ViewContext vc;
2795         BMEditMesh *em;
2796         BMesh *bm;
2797         BMVert *eve;
2798         BMEdge *eed;
2799         BMFace *efa;
2800         const bool sel = !RNA_boolean_get(op->ptr, "deselect");
2801         int index;
2802
2803         if (RNA_struct_property_is_set(op->ptr, "index")) {
2804                 return edbm_select_linked_pick_exec(C, op);
2805         }
2806
2807         /* unified_finednearest needs ogl */
2808         view3d_operator_needs_opengl(C);
2809
2810         /* setup view context for argument to callbacks */
2811         em_setup_viewcontext(C, &vc);
2812         em = vc.em;
2813         bm = em->bm;
2814
2815         if (bm->totedge == 0) {
2816                 return OPERATOR_CANCELLED;
2817         }
2818
2819         vc.mval[0] = event->mval[0];
2820         vc.mval[1] = event->mval[1];
2821
2822         /* return warning! */
2823         if (unified_findnearest(&vc, &eve, &eed, &efa) == 0) {
2824                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2825
2826                 return OPERATOR_CANCELLED;
2827         }
2828
2829 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2830         int delimit = select_linked_delimit_default_from_op(op, em);
2831 #else
2832         int delimit = RNA_enum_get(op->ptr, "delimit");
2833 #endif
2834
2835         BMElem *ele = EDBM_elem_from_selectmode(em, eve, eed, efa);
2836
2837         edbm_select_linked_pick_ex(em, ele, sel, delimit);
2838
2839         /* to support redo */
2840         BM_mesh_elem_index_ensure(bm, ele->head.htype);
2841         index = EDBM_elem_to_index_any(em, ele);
2842
2843         RNA_int_set(op->ptr, "index", index);
2844
2845         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2846
2847         return OPERATOR_FINISHED;
2848 }
2849
2850
2851 static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op)
2852 {
2853         Object *obedit = CTX_data_edit_object(C);
2854         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2855         BMesh *bm = em->bm;
2856         int index;
2857         const bool sel = !RNA_boolean_get(op->ptr, "deselect");
2858
2859         index = RNA_int_get(op->ptr, "index");
2860         if (index < 0 || index >= (bm->totvert + bm->totedge + bm->totface)) {
2861                 return OPERATOR_CANCELLED;
2862         }
2863
2864         BMElem *ele = EDBM_elem_from_index_any(em, index);
2865
2866 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2867         int delimit = select_linked_delimit_default_from_op(op, em);
2868 #else
2869         int delimit = RNA_enum_get(op->ptr, "delimit");
2870 #endif
2871
2872         edbm_select_linked_pick_ex(em, ele, sel, delimit);
2873
2874         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2875
2876         return OPERATOR_FINISHED;
2877 }
2878
2879 void MESH_OT_select_linked_pick(wmOperatorType *ot)
2880 {
2881         PropertyRNA *prop;
2882
2883         /* identifiers */
2884         ot->name = "Select Linked";
2885         ot->idname = "MESH_OT_select_linked_pick";
2886         ot->description = "(De)select all vertices linked to the edge under the mouse cursor";
2887         
2888         /* api callbacks */
2889         ot->invoke = edbm_select_linked_pick_invoke;
2890         ot->exec = edbm_select_linked_pick_exec;
2891         ot->poll = ED_operator_editmesh;
2892         
2893         /* flags */
2894         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2895         
2896         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
2897         prop = RNA_def_enum_flag(ot->srna, "delimit", rna_enum_mesh_delimit_mode_items, BMO_DELIM_SEAM, "Delimit",
2898                                  "Delimit selected region");
2899 #ifdef USE_LINKED_SELECT_DEFAULT_HACK
2900         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2901 #endif
2902
2903         /* use for redo */
2904         prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
2905         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
2906 }
2907
2908
2909 static int edbm_select_face_by_sides_exec(bContext *C, wmOperator *op)
2910 {
2911         Object *obedit = CTX_data_edit_object(C);
2912         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2913         BMFace *efa;
2914         BMIter iter;
2915         const int numverts = RNA_int_get(op->ptr, "number");
2916         const int type = RNA_enum_get(op->ptr, "type");
2917
2918         if (!RNA_boolean_get(op->ptr, "extend"))
2919                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
2920
2921         BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2922
2923                 bool select;
2924
2925                 switch (type) {
2926                         case 0:
2927                                 select = (efa->len < numverts);
2928                                 break;
2929                         case 1:
2930                                 select = (efa->len == numverts);
2931                                 break;
2932                         case 2:
2933                                 select = (efa->len > numverts);
2934                                 break;
2935                         case 3:
2936                                 select = (efa->len != numverts);
2937                                 break;
2938                         default:
2939                                 BLI_assert(0);
2940                                 select = false;
2941                                 break;
2942                 }
2943
2944                 if (select) {
2945                         BM_face_select_set(em->bm, efa, true);
2946                 }
2947         }
2948
2949         EDBM_selectmode_flush(em);
2950
2951         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2952         return OPERATOR_FINISHED;
2953 }
2954
2955 void MESH_OT_select_face_by_sides(wmOperatorType *ot)
2956 {
2957         static const EnumPropertyItem type_items[] = {
2958                 {0, "LESS", 0, "Less Than", ""},
2959                 {1, "EQUAL", 0, "Equal To", ""},
2960                 {2, "GREATER", 0, "Greater Than", ""},
2961                 {3, "NOTEQUAL", 0, "Not Equal To", ""},
2962                 {0, NULL, 0, NULL, NULL}
2963         };
2964
2965         /* identifiers */
2966         ot->name = "Select Faces by Sides";
2967         ot->description = "Select vertices or faces by the number of polygon sides";
2968         ot->idname = "MESH_OT_select_face_by_sides";
2969
2970         /* api callbacks */
2971         ot->exec = edbm_select_face_by_sides_exec;
2972         ot->poll = ED_operator_editmesh;
2973
2974         /* flags */
2975         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2976
2977         /* properties */
2978         RNA_def_int(ot->srna, "number", 4, 3, INT_MAX, "Number of Vertices", "", 3, INT_MAX);
2979         RNA_def_enum(ot->srna, "type", type_items, 1, "Type", "Type of comparison to make");
2980         RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection");
2981 }
2982
2983
2984 static int edbm_select_loose_exec(bContext *C, wmOperator *op)
2985 {
2986         Object *obedit = CTX_data_edit_object(C);
2987         BMEditMesh *em = BKE_editmesh_from_object(obedit);
2988         BMesh *bm = em->bm;
2989         BMIter iter;
2990
2991         if (!RNA_boolean_get(op->ptr, "extend"))
2992                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
2993
2994         if (em->selectmode & SCE_SELECT_VERTEX) {
2995                 BMVert *eve;
2996                 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2997                         if (!eve->e) {
2998                                 BM_vert_select_set(bm, eve, true);
2999                         }
3000                 }
3001         }
3002
3003         if (em->selectmode & SCE_SELECT_EDGE) {
3004                 BMEdge *eed;
3005                 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
3006                         if (BM_edge_is_wire(eed)) {
3007                                 BM_edge_select_set(bm, eed, true);
3008                         }
3009                 }
3010         }
3011
3012         if (em->selectmode & SCE_SELECT_FACE) {
3013                 BMFace *efa;
3014                 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3015                         BMIter liter;
3016                         BMLoop *l;
3017                         bool is_loose = true;
3018                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3019                                 if (!BM_edge_is_boundary(l->e)) {
3020                                         is_loose = false;
3021                                         break;
3022                                 }
3023                         }
3024                         if (is_loose) {
3025                                 BM_face_select_set(bm, efa, true);
3026                         }
3027                 }
3028         }
3029
3030         EDBM_selectmode_flush(em);
3031
3032         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3033         return OPERATOR_FINISHED;
3034 }
3035
3036 void MESH_OT_select_loose(wmOperatorType *ot)
3037 {
3038         /* identifiers */
3039         ot->name = "Select Loose Geometry";
3040         ot->description = "Select loose geometry based on the selection mode";
3041         ot->idname = "MESH_OT_select_loose";
3042
3043         /* api callbacks */
3044         ot->exec = edbm_select_loose_exec;
3045         ot->poll = ED_operator_editmesh;
3046
3047         /* flags */
3048         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3049
3050         /* props */
3051         RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
3052 }
3053
3054
3055 static int edbm_select_mirror_exec(bContext *C, wmOperator *op)
3056 {
3057         Object *obedit = CTX_data_edit_object(C);
3058         BMEditMesh *em = BKE_editmesh_from_object(obedit);
3059         const int axis_flag = RNA_enum_get(op->ptr, "axis");
3060         const bool extend = RNA_boolean_get(op->ptr, "extend");
3061
3062         if (em->bm->totvert && em->bm->totvertsel) {
3063                 int totmirr, totfail;
3064
3065                 for (int axis = 0; axis < 3; axis++) {
3066                         if ((1 << axis) & axis_flag) {
3067                                 EDBM_select_mirrored(em, axis, extend, &totmirr, &totfail);
3068                         }
3069                 }
3070
3071                 if (totmirr) {
3072                         EDBM_selectmode_flush(em);
3073                         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3074                 }
3075
3076                 ED_mesh_report_mirror_ex(op, totmirr, totfail, em->bm->selectmode);
3077         }
3078
3079         return OPERATOR_FINISHED;
3080 }
3081
3082 void MESH_OT_select_mirror(wmOperatorType *ot)
3083 {
3084         /* identifiers */
3085         ot->name = "Select Mirror";
3086         ot->description = "Select mesh items at mirrored locations";
3087         ot->idname = "MESH_OT_select_mirror";
3088
3089         /* api callbacks */
3090         ot->exec = edbm_select_mirror_exec;
3091         ot->poll = ED_operator_editmesh;
3092
3093         /* flags */
3094         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3095
3096         /* props */
3097         RNA_def_enum_flag(ot->srna, "axis", rna_enum_axis_flag_xyz_items, (1 << 0), "Axis", "");
3098
3099         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the existing selection");
3100 }
3101
3102 /* ******************** **************** */
3103
3104 static int edbm_select_more_exec(bContext *C, wmOperator *op)
3105 {
3106         Object *obedit = CTX_data_edit_object(C);
3107         BMEditMesh *em = BKE_editmesh_from_object(obedit);
3108         const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
3109
3110         EDBM_select_more(em, use_face_step);
3111
3112         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3113         return OPERATOR_FINISHED;
3114 }
3115
3116 void MESH_OT_select_more(wmOperatorType *ot)
3117 {
3118         /* identifiers */
3119         ot->name = "Select More";
3120         ot->idname = "MESH_OT_select_more";
3121         ot->description = "Select more vertices, edges or faces connected to initial selection";
3122
3123         /* api callbacks */
3124         ot->exec = edbm_select_more_exec;
3125         ot->poll = ED_operator_editmesh;