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