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