ace427f65ab3c4ca2a81d95c11ac3cbd56a20f6f
[blender-staging.git] / source / blender / editors / mask / mask_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) 2012 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation,
23  *                 Sergey Sharybin
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/mask/mask_select.c
29  *  \ingroup edmask
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_utildefines.h"
35 #include "BLI_rect.h"
36 #include "BLI_lasso.h"
37
38 #include "BKE_context.h"
39 #include "BKE_mask.h"
40
41 #include "DNA_mask_types.h"
42 #include "DNA_object_types.h"  /* SELECT */
43
44 #include "WM_api.h"
45 #include "WM_types.h"
46
47 #include "ED_screen.h"
48 #include "ED_clip.h"
49 #include "ED_mask.h"  /* own include */
50
51 #include "RNA_access.h"
52 #include "RNA_define.h"
53
54 #include "mask_intern.h"  /* own include */
55
56 /* 'check' select */
57 int ED_mask_spline_select_check(MaskSpline *spline)
58 {
59         int i;
60
61         for (i = 0; i < spline->tot_point; i++) {
62                 MaskSplinePoint *point = &spline->points[i];
63
64                 if (MASKPOINT_ISSEL_ANY(point))
65                         return TRUE;
66         }
67
68         return FALSE;
69 }
70
71 int ED_mask_layer_select_check(MaskLayer *masklay)
72 {
73         MaskSpline *spline;
74
75         if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
76                 return FALSE;
77         }
78
79         for (spline = masklay->splines.first; spline; spline = spline->next) {
80                 if (ED_mask_spline_select_check(spline)) {
81                         return TRUE;
82                 }
83         }
84
85         return FALSE;
86 }
87
88 int ED_mask_select_check(Mask *mask)
89 {
90         MaskLayer *masklay;
91
92         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
93                 if (ED_mask_layer_select_check(masklay)) {
94                         return TRUE;
95                 }
96         }
97
98         return FALSE;
99 }
100
101 /* 'sel' select  */
102 void ED_mask_spline_select_set(MaskSpline *spline, const short do_select)
103 {
104         int i;
105
106         if (do_select)
107                 spline->flag |= SELECT;
108         else
109                 spline->flag &= ~SELECT;
110
111         for (i = 0; i < spline->tot_point; i++) {
112                 MaskSplinePoint *point = &spline->points[i];
113
114                 BKE_mask_point_select_set(point, do_select);
115         }
116 }
117
118 void ED_mask_layer_select_set(MaskLayer *masklay, const short do_select)
119 {
120         MaskSpline *spline;
121
122         if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
123                 if (do_select == TRUE) {
124                         return;
125                 }
126         }
127
128         for (spline = masklay->splines.first; spline; spline = spline->next) {
129                 ED_mask_spline_select_set(spline, do_select);
130         }
131 }
132
133 void ED_mask_select_toggle_all(Mask *mask, int action)
134 {
135         MaskLayer *masklay;
136
137         if (action == SEL_TOGGLE) {
138                 if (ED_mask_select_check(mask))
139                         action = SEL_DESELECT;
140                 else
141                         action = SEL_SELECT;
142         }
143
144         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
145
146                 if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
147                         continue;
148                 }
149
150                 if (action == SEL_INVERT) {
151                         /* we don't have generic functons for this, its restricted to this operator
152                          * if one day we need to re-use such functionality, they can be split out */
153
154                         MaskSpline *spline;
155                         if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
156                                 continue;
157                         }
158                         for (spline = masklay->splines.first; spline; spline = spline->next) {
159                                 int i;
160                                 for (i = 0; i < spline->tot_point; i++) {
161                                         MaskSplinePoint *point = &spline->points[i];
162                                         BKE_mask_point_select_set(point, !MASKPOINT_ISSEL_ANY(point));
163                                 }
164                         }
165
166                 }
167                 else {
168                         ED_mask_layer_select_set(masklay, (action == SEL_SELECT) ? TRUE : FALSE);
169                 }
170         }
171 }
172
173 void ED_mask_select_flush_all(Mask *mask)
174 {
175         MaskLayer *masklay;
176
177         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
178                 MaskSpline *spline;
179
180                 for (spline = masklay->splines.first; spline; spline = spline->next) {
181                         int i;
182
183                         spline->flag &= ~SELECT;
184
185                         /* intentionally _dont_ do this in the masklay loop
186                          * so we clear flags on all splines */
187                         if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
188                                 continue;
189                         }
190
191                         for (i = 0; i < spline->tot_point; i++) {
192                                 MaskSplinePoint *cur_point = &spline->points[i];
193
194                                 if (MASKPOINT_ISSEL_ANY(cur_point)) {
195                                         spline->flag |= SELECT;
196                                 }
197                                 else {
198                                         int j;
199
200                                         for (j = 0; j < cur_point->tot_uw; j++) {
201                                                 if (cur_point->uw[j].flag & SELECT) {
202                                                         spline->flag |= SELECT;
203                                                         break;
204                                                 }
205                                         }
206                                 }
207                         }
208                 }
209         }
210 }
211
212 /******************** toggle selection *********************/
213
214 static int select_all_exec(bContext *C, wmOperator *op)
215 {
216         Mask *mask = CTX_data_edit_mask(C);
217         int action = RNA_enum_get(op->ptr, "action");
218
219         ED_mask_select_toggle_all(mask, action);
220         ED_mask_select_flush_all(mask);
221
222         WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
223
224         return OPERATOR_FINISHED;
225 }
226
227 void MASK_OT_select_all(wmOperatorType *ot)
228 {
229         /* identifiers */
230         ot->name = "(De)select All";
231         ot->description = "Change selection of all curve points";
232         ot->idname = "MASK_OT_select_all";
233
234         /* api callbacks */
235         ot->exec = select_all_exec;
236         ot->poll = ED_maskedit_mask_poll;
237
238         /* flags */
239         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
240
241         /* properties */
242         WM_operator_properties_select_all(ot);
243 }
244
245 /******************** select *********************/
246
247 static int select_exec(bContext *C, wmOperator *op)
248 {
249         Mask *mask = CTX_data_edit_mask(C);
250         MaskLayer *masklay;
251         MaskSpline *spline;
252         MaskSplinePoint *point = NULL;
253         float co[2];
254         short extend = RNA_boolean_get(op->ptr, "extend");
255         short deselect = RNA_boolean_get(op->ptr, "deselect");
256         short toggle = RNA_boolean_get(op->ptr, "toggle");
257
258         int is_handle = 0;
259         const float threshold = 19;
260
261         RNA_float_get_array(op->ptr, "location", co);
262
263         point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &is_handle, NULL);
264
265         if (extend == 0 && deselect == 0 && toggle == 0)
266                 ED_mask_select_toggle_all(mask, SEL_DESELECT);
267
268         if (point) {
269
270                 if (is_handle) {
271                         if (extend) {
272                                 masklay->act_spline = spline;
273                                 masklay->act_point = point;
274
275                                 BKE_mask_point_select_set_handle(point, TRUE);
276                         }
277                         else if (deselect) {
278                                 BKE_mask_point_select_set_handle(point, FALSE);
279                         }
280                         else {
281                                 masklay->act_spline = spline;
282                                 masklay->act_point = point;
283
284                                 if (!MASKPOINT_ISSEL_HANDLE(point)) {
285                                         BKE_mask_point_select_set_handle(point, TRUE);
286                                 }
287                                 else if (toggle) {
288                                         BKE_mask_point_select_set_handle(point, FALSE);
289                                 }
290                         }
291                 }
292                 else {
293                         if (extend) {
294                                 masklay->act_spline = spline;
295                                 masklay->act_point = point;
296
297                                 BKE_mask_point_select_set(point, TRUE);
298                         }
299                         else if (deselect) {
300                                 BKE_mask_point_select_set(point, FALSE);
301                         }
302                         else {
303                                 masklay->act_spline = spline;
304                                 masklay->act_point = point;
305
306                                 if (!MASKPOINT_ISSEL_ANY(point)) {
307                                         BKE_mask_point_select_set(point, TRUE);
308                                 }
309                                 else if (toggle) {
310                                         BKE_mask_point_select_set(point, FALSE);
311                                 }
312                         }
313                 }
314
315                 masklay->act_spline = spline;
316                 masklay->act_point = point;
317
318                 ED_mask_select_flush_all(mask);
319
320                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
321
322                 return OPERATOR_FINISHED;
323         }
324         else {
325                 MaskSplinePointUW *uw;
326
327                 if (ED_mask_feather_find_nearest(C, mask, co, threshold, &masklay, &spline, &point, &uw, NULL)) {
328
329                         if (extend) {
330                                 masklay->act_spline = spline;
331                                 masklay->act_point = point;
332
333                                 if (uw) uw->flag |= SELECT;
334                         }
335                         else if (deselect) {
336                                 if (uw) uw->flag &= ~SELECT;
337                         }
338                         else {
339                                 masklay->act_spline = spline;
340                                 masklay->act_point = point;
341
342                                 if (uw) {
343                                         if (!(uw->flag & SELECT)) {
344                                                 uw->flag |= SELECT;
345                                         }
346                                         else if (toggle) {
347                                                 uw->flag &= ~SELECT;
348                                         }
349                                 }
350                         }
351
352                         ED_mask_select_flush_all(mask);
353
354                         WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
355
356                         return OPERATOR_FINISHED;
357                 }
358         }
359
360         return OPERATOR_PASS_THROUGH;
361 }
362
363 static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
364 {
365         ScrArea *sa = CTX_wm_area(C);
366         ARegion *ar = CTX_wm_region(C);
367
368         float co[2];
369
370         ED_mask_mouse_pos(sa, ar, event->mval, co);
371
372         RNA_float_set_array(op->ptr, "location", co);
373
374         return select_exec(C, op);
375 }
376
377 void MASK_OT_select(wmOperatorType *ot)
378 {
379         /* identifiers */
380         ot->name = "Select";
381         ot->description = "Select spline points";
382         ot->idname = "MASK_OT_select";
383
384         /* api callbacks */
385         ot->exec = select_exec;
386         ot->invoke = select_invoke;
387         ot->poll = ED_maskedit_mask_poll;
388
389         /* flags */
390         ot->flag = OPTYPE_UNDO;
391
392         /* properties */
393         WM_operator_properties_mouse_select(ot);
394
395         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
396                              "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
397 }
398
399
400
401 /********************** border select operator *********************/
402
403 static int border_select_exec(bContext *C, wmOperator *op)
404 {
405         ScrArea *sa = CTX_wm_area(C);
406         ARegion *ar = CTX_wm_region(C);
407
408         Mask *mask = CTX_data_edit_mask(C);
409         MaskLayer *masklay;
410         int i;
411
412         rcti rect;
413         rctf rectf;
414         int change = FALSE, mode, extend;
415
416         /* get rectangle from operator */
417         WM_operator_properties_border_to_rcti(op, &rect);
418
419         ED_mask_point_pos(sa, ar, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
420         ED_mask_point_pos(sa, ar, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
421
422         mode = RNA_int_get(op->ptr, "gesture_mode");
423         extend = RNA_boolean_get(op->ptr, "extend");
424
425         /* do actual selection */
426         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
427                 MaskSpline *spline;
428
429                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
430                         continue;
431                 }
432
433                 for (spline = masklay->splines.first; spline; spline = spline->next) {
434                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
435
436                         for (i = 0; i < spline->tot_point; i++) {
437                                 MaskSplinePoint *point = &spline->points[i];
438                                 MaskSplinePoint *point_deform = &points_array[i];
439
440                                 /* TODO: handles? */
441                                 /* TODO: uw? */
442
443                                 if (BLI_rctf_isect_pt_v(&rectf, point_deform->bezt.vec[1])) {
444                                         BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
445                                         BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT);
446                                 }
447                                 else if (!extend) {
448                                         BKE_mask_point_select_set(point, FALSE);
449                                         BKE_mask_point_select_set_handle(point, FALSE);
450                                 }
451
452                                 change = TRUE;
453                         }
454                 }
455         }
456
457         if (change) {
458                 ED_mask_select_flush_all(mask);
459
460                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
461
462                 return OPERATOR_FINISHED;
463         }
464
465         return OPERATOR_CANCELLED;
466 }
467
468 void MASK_OT_select_border(wmOperatorType *ot)
469 {
470         /* identifiers */
471         ot->name = "Border Select";
472         ot->description = "Select markers using border selection";
473         ot->idname = "MASK_OT_select_border";
474
475         /* api callbacks */
476         ot->invoke = WM_border_select_invoke;
477         ot->exec = border_select_exec;
478         ot->modal = WM_border_select_modal;
479         ot->poll = ED_maskedit_mask_poll;
480
481         /* flags */
482         ot->flag = OPTYPE_UNDO;
483
484         /* properties */
485         WM_operator_properties_gesture_border(ot, TRUE);
486 }
487
488 static int do_lasso_select_mask(bContext *C, int mcords[][2], short moves, short select)
489 {
490         ScrArea *sa = CTX_wm_area(C);
491         ARegion *ar = CTX_wm_region(C);
492
493         Mask *mask = CTX_data_edit_mask(C);
494         MaskLayer *masklay;
495         int i;
496
497         rcti rect;
498         int change = FALSE;
499
500         /* get rectangle from operator */
501         BLI_lasso_boundbox(&rect, mcords, moves);
502
503         /* do actual selection */
504         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
505                 MaskSpline *spline;
506
507                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
508                         continue;
509                 }
510
511                 for (spline = masklay->splines.first; spline; spline = spline->next) {
512                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
513
514                         for (i = 0; i < spline->tot_point; i++) {
515                                 MaskSplinePoint *point = &spline->points[i];
516                                 MaskSplinePoint *point_deform = &points_array[i];
517
518                                 /* TODO: handles? */
519                                 /* TODO: uw? */
520
521                                 float screen_co[2];
522
523                                 /* marker in screen coords */
524                                 ED_mask_point_pos__reverse(sa, ar,
525                                                            point_deform->bezt.vec[1][0], point_deform->bezt.vec[1][1],
526                                                            &screen_co[0], &screen_co[1]);
527
528                                 if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
529                                     BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX))
530                                 {
531                                         BKE_mask_point_select_set(point, select);
532                                         BKE_mask_point_select_set_handle(point, select);
533                                 }
534
535                                 change = TRUE;
536                         }
537                 }
538         }
539
540         if (change) {
541                 ED_mask_select_flush_all(mask);
542
543                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
544         }
545
546         return change;
547 }
548
549 static int clip_lasso_select_exec(bContext *C, wmOperator *op)
550 {
551         int mcords_tot;
552         int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
553
554         if (mcords) {
555                 short select;
556
557                 select = !RNA_boolean_get(op->ptr, "deselect");
558                 do_lasso_select_mask(C, mcords, mcords_tot, select);
559
560                 MEM_freeN(mcords);
561
562                 return OPERATOR_FINISHED;
563         }
564         return OPERATOR_PASS_THROUGH;
565 }
566
567 void MASK_OT_select_lasso(wmOperatorType *ot)
568 {
569         /* identifiers */
570         ot->name = "Lasso Select";
571         ot->description = "Select markers using lasso selection";
572         ot->idname = "MASK_OT_select_lasso";
573
574         /* api callbacks */
575         ot->invoke = WM_gesture_lasso_invoke;
576         ot->modal = WM_gesture_lasso_modal;
577         ot->exec = clip_lasso_select_exec;
578         ot->poll = ED_maskedit_mask_poll;
579         ot->cancel = WM_gesture_lasso_cancel;
580
581         /* flags */
582         ot->flag = OPTYPE_UNDO;
583
584         /* properties */
585         RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
586         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items");
587         RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
588 }
589
590 /********************** circle select operator *********************/
591
592 static int mask_spline_point_inside_ellipse(BezTriple *bezt, float offset[2], float ellipse[2])
593 {
594         /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
595         float x, y;
596
597         x = (bezt->vec[1][0] - offset[0]) * ellipse[0];
598         y = (bezt->vec[1][1] - offset[1]) * ellipse[1];
599
600         return x * x + y * y < 1.0f;
601 }
602
603 static int circle_select_exec(bContext *C, wmOperator *op)
604 {
605         ScrArea *sa = CTX_wm_area(C);
606         ARegion *ar = CTX_wm_region(C);
607
608         Mask *mask = CTX_data_edit_mask(C);
609         MaskLayer *masklay;
610         int i;
611
612         int x, y, radius, width, height, mode, change = FALSE;
613         float zoomx, zoomy, offset[2], ellipse[2];
614
615         /* get operator properties */
616         x = RNA_int_get(op->ptr, "x");
617         y = RNA_int_get(op->ptr, "y");
618         radius = RNA_int_get(op->ptr, "radius");
619
620         mode = RNA_int_get(op->ptr, "gesture_mode");
621
622         /* compute ellipse and position in unified coordinates */
623         ED_mask_get_size(sa, &width, &height);
624         ED_mask_zoom(sa, ar, &zoomx, &zoomy);
625         width = height = MAX2(width, height);
626
627         ellipse[0] = width * zoomx / radius;
628         ellipse[1] = height * zoomy / radius;
629
630         ED_mask_point_pos(sa, ar, x, y, &offset[0], &offset[1]);
631
632         /* do actual selection */
633         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
634                 MaskSpline *spline;
635
636                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
637                         continue;
638                 }
639
640                 for (spline = masklay->splines.first; spline; spline = spline->next) {
641                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
642
643                         for (i = 0; i < spline->tot_point; i++) {
644                                 MaskSplinePoint *point = &spline->points[i];
645                                 MaskSplinePoint *point_deform = &points_array[i];
646
647                                 if (mask_spline_point_inside_ellipse(&point_deform->bezt, offset, ellipse)) {
648                                         BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
649                                         BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT);
650
651                                         change = TRUE;
652                                 }
653                         }
654                 }
655         }
656
657         if (change) {
658                 ED_mask_select_flush_all(mask);
659
660                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
661
662                 return OPERATOR_FINISHED;
663         }
664
665         return OPERATOR_CANCELLED;
666 }
667
668 void MASK_OT_select_circle(wmOperatorType *ot)
669 {
670         /* identifiers */
671         ot->name = "Circle Select";
672         ot->description = "Select markers using circle selection";
673         ot->idname = "MASK_OT_select_circle";
674
675         /* api callbacks */
676         ot->invoke = WM_gesture_circle_invoke;
677         ot->modal = WM_gesture_circle_modal;
678         ot->exec = circle_select_exec;
679         ot->poll = ED_maskedit_mask_poll;
680
681         /* flags */
682         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
683
684         /* properties */
685         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
686         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
687         RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
688         RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
689 }
690
691 static int mask_select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event)
692 {
693         ScrArea *sa = CTX_wm_area(C);
694         ARegion *ar = CTX_wm_region(C);
695
696         Mask *mask = CTX_data_edit_mask(C);
697         MaskLayer *masklay;
698         MaskSpline *spline;
699         MaskSplinePoint *point = NULL;
700         float co[2];
701         int do_select = !RNA_boolean_get(op->ptr, "deselect");
702
703         int is_handle = 0;
704         const float threshold = 19;
705         int change = FALSE;
706
707         ED_mask_mouse_pos(sa, ar, event->mval, co);
708
709         point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &is_handle, NULL);
710
711         if (point) {
712                 ED_mask_spline_select_set(spline, do_select);
713                 masklay->act_spline = spline;
714                 masklay->act_point = point;
715
716                 change = TRUE;
717         }
718
719         if (change) {
720                 ED_mask_select_flush_all(mask);
721
722                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
723
724                 return OPERATOR_FINISHED;
725         }
726
727         return OPERATOR_CANCELLED;
728 }
729
730 void MASK_OT_select_linked_pick(wmOperatorType *ot)
731 {
732         /* identifiers */
733         ot->name = "Select Linked";
734         ot->idname = "MASK_OT_select_linked_pick";
735         ot->description = "(De)select all points linked to the curve under the mouse cursor";
736
737         /* api callbacks */
738         ot->invoke = mask_select_linked_pick_invoke;
739         ot->poll = ED_maskedit_mask_poll;
740
741         /* flags */
742         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
743
744         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
745 }
746
747 static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
748 {
749         Mask *mask = CTX_data_edit_mask(C);
750         MaskLayer *masklay;
751
752         int change = FALSE;
753
754         /* do actual selection */
755         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
756                 MaskSpline *spline;
757
758                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
759                         continue;
760                 }
761
762                 for (spline = masklay->splines.first; spline; spline = spline->next) {
763                         if (ED_mask_spline_select_check(spline)) {
764                                 ED_mask_spline_select_set(spline, TRUE);
765                                 change = TRUE;
766                         }
767                 }
768         }
769
770         if (change) {
771                 ED_mask_select_flush_all(mask);
772
773                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
774
775                 return OPERATOR_FINISHED;
776         }
777
778         return OPERATOR_CANCELLED;
779 }
780
781 void MASK_OT_select_linked(wmOperatorType *ot)
782 {
783         /* identifiers */
784         ot->name = "Select Linked All";
785         ot->idname = "MASK_OT_select_linked";
786         ot->description = "Select all vertices linked to the active mesh";
787
788         /* api callbacks */
789         ot->exec = mask_select_linked_exec;
790         ot->poll = ED_maskedit_mask_poll;
791
792         /* flags */
793         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
794 }