doxygen: add newline after \file
[blender.git] / source / blender / editors / mask / mask_select.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2012 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup edmask
22  */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "BLI_utildefines.h"
27 #include "BLI_rect.h"
28 #include "BLI_lasso_2d.h"
29 #include "BLI_math.h"
30
31 #include "BKE_context.h"
32 #include "BKE_mask.h"
33
34 #include "DNA_mask_types.h"
35
36 #include "WM_api.h"
37 #include "WM_types.h"
38
39 #include "ED_screen.h"
40 #include "ED_select_utils.h"
41 #include "ED_mask.h"  /* own include */
42
43 #include "RNA_access.h"
44 #include "RNA_define.h"
45
46 #include "mask_intern.h"  /* own include */
47
48 /* 'check' select */
49 bool ED_mask_spline_select_check(MaskSpline *spline)
50 {
51         int i;
52
53         for (i = 0; i < spline->tot_point; i++) {
54                 MaskSplinePoint *point = &spline->points[i];
55
56                 if (MASKPOINT_ISSEL_ANY(point))
57                         return true;
58         }
59
60         return false;
61 }
62
63 bool ED_mask_layer_select_check(MaskLayer *masklay)
64 {
65         MaskSpline *spline;
66
67         if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
68                 return false;
69         }
70
71         for (spline = masklay->splines.first; spline; spline = spline->next) {
72                 if (ED_mask_spline_select_check(spline)) {
73                         return true;
74                 }
75         }
76
77         return false;
78 }
79
80 bool ED_mask_select_check(Mask *mask)
81 {
82         MaskLayer *masklay;
83
84         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
85                 if (ED_mask_layer_select_check(masklay)) {
86                         return true;
87                 }
88         }
89
90         return false;
91 }
92
93 /* 'sel' select  */
94 void ED_mask_spline_select_set(MaskSpline *spline, const bool do_select)
95 {
96         int i;
97
98         if (do_select)
99                 spline->flag |= SELECT;
100         else
101                 spline->flag &= ~SELECT;
102
103         for (i = 0; i < spline->tot_point; i++) {
104                 MaskSplinePoint *point = &spline->points[i];
105
106                 BKE_mask_point_select_set(point, do_select);
107         }
108 }
109
110 void ED_mask_layer_select_set(MaskLayer *masklay, const bool do_select)
111 {
112         MaskSpline *spline;
113
114         if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
115                 if (do_select == true) {
116                         return;
117                 }
118         }
119
120         for (spline = masklay->splines.first; spline; spline = spline->next) {
121                 ED_mask_spline_select_set(spline, do_select);
122         }
123 }
124
125 void ED_mask_select_toggle_all(Mask *mask, int action)
126 {
127         MaskLayer *masklay;
128
129         if (action == SEL_TOGGLE) {
130                 if (ED_mask_select_check(mask))
131                         action = SEL_DESELECT;
132                 else
133                         action = SEL_SELECT;
134         }
135
136         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
137
138                 if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
139                         continue;
140                 }
141
142                 if (action == SEL_INVERT) {
143                         /* we don't have generic functions for this, its restricted to this operator
144                          * if one day we need to re-use such functionality, they can be split out */
145
146                         MaskSpline *spline;
147                         if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
148                                 continue;
149                         }
150                         for (spline = masklay->splines.first; spline; spline = spline->next) {
151                                 int i;
152                                 for (i = 0; i < spline->tot_point; i++) {
153                                         MaskSplinePoint *point = &spline->points[i];
154                                         BKE_mask_point_select_set(point, !MASKPOINT_ISSEL_ANY(point));
155                                 }
156                         }
157
158                 }
159                 else {
160                         ED_mask_layer_select_set(masklay, (action == SEL_SELECT) ? true : false);
161                 }
162         }
163 }
164
165 void ED_mask_select_flush_all(Mask *mask)
166 {
167         MaskLayer *masklay;
168
169         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
170                 MaskSpline *spline;
171
172                 for (spline = masklay->splines.first; spline; spline = spline->next) {
173                         int i;
174
175                         spline->flag &= ~SELECT;
176
177                         /* intentionally _dont_ do this in the masklay loop
178                          * so we clear flags on all splines */
179                         if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
180                                 continue;
181                         }
182
183                         for (i = 0; i < spline->tot_point; i++) {
184                                 MaskSplinePoint *cur_point = &spline->points[i];
185
186                                 if (MASKPOINT_ISSEL_ANY(cur_point)) {
187                                         spline->flag |= SELECT;
188                                 }
189                                 else {
190                                         int j;
191
192                                         for (j = 0; j < cur_point->tot_uw; j++) {
193                                                 if (cur_point->uw[j].flag & SELECT) {
194                                                         spline->flag |= SELECT;
195                                                         break;
196                                                 }
197                                         }
198                                 }
199                         }
200                 }
201         }
202 }
203
204 /******************** toggle selection *********************/
205
206 static int select_all_exec(bContext *C, wmOperator *op)
207 {
208         Mask *mask = CTX_data_edit_mask(C);
209         int action = RNA_enum_get(op->ptr, "action");
210
211         ED_mask_select_toggle_all(mask, action);
212         ED_mask_select_flush_all(mask);
213
214         WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
215
216         return OPERATOR_FINISHED;
217 }
218
219 void MASK_OT_select_all(wmOperatorType *ot)
220 {
221         /* identifiers */
222         ot->name = "(De)select All";
223         ot->description = "Change selection of all curve points";
224         ot->idname = "MASK_OT_select_all";
225
226         /* api callbacks */
227         ot->exec = select_all_exec;
228         ot->poll = ED_maskedit_mask_poll;
229
230         /* flags */
231         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
232
233         /* properties */
234         WM_operator_properties_select_all(ot);
235 }
236
237 /******************** select *********************/
238
239 static int select_exec(bContext *C, wmOperator *op)
240 {
241         Mask *mask = CTX_data_edit_mask(C);
242         MaskLayer *masklay;
243         MaskSpline *spline;
244         MaskSplinePoint *point = NULL;
245         float co[2];
246         bool extend = RNA_boolean_get(op->ptr, "extend");
247         bool deselect = RNA_boolean_get(op->ptr, "deselect");
248         bool toggle = RNA_boolean_get(op->ptr, "toggle");
249         eMaskWhichHandle which_handle;
250         const float threshold = 19;
251
252         RNA_float_get_array(op->ptr, "location", co);
253
254         point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &which_handle, NULL);
255
256         if (extend == false && deselect == false && toggle == false)
257                 ED_mask_select_toggle_all(mask, SEL_DESELECT);
258
259         if (point) {
260                 if (which_handle != MASK_WHICH_HANDLE_NONE) {
261                         if (extend) {
262                                 masklay->act_spline = spline;
263                                 masklay->act_point = point;
264
265                                 BKE_mask_point_select_set_handle(point, which_handle, true);
266                         }
267                         else if (deselect) {
268                                 BKE_mask_point_select_set_handle(point, which_handle, false);
269                         }
270                         else {
271                                 masklay->act_spline = spline;
272                                 masklay->act_point = point;
273
274                                 if (!MASKPOINT_ISSEL_HANDLE(point, which_handle)) {
275                                         BKE_mask_point_select_set_handle(point, which_handle, true);
276                                 }
277                                 else if (toggle) {
278                                         BKE_mask_point_select_set_handle(point, which_handle, false);
279                                 }
280                         }
281                 }
282                 else {
283                         if (extend) {
284                                 masklay->act_spline = spline;
285                                 masklay->act_point = point;
286
287                                 BKE_mask_point_select_set(point, true);
288                         }
289                         else if (deselect) {
290                                 BKE_mask_point_select_set(point, false);
291                         }
292                         else {
293                                 masklay->act_spline = spline;
294                                 masklay->act_point = point;
295
296                                 if (!MASKPOINT_ISSEL_ANY(point)) {
297                                         BKE_mask_point_select_set(point, true);
298                                 }
299                                 else if (toggle) {
300                                         BKE_mask_point_select_set(point, false);
301                                 }
302                         }
303                 }
304
305                 masklay->act_spline = spline;
306                 masklay->act_point = point;
307
308                 ED_mask_select_flush_all(mask);
309
310                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
311
312                 return OPERATOR_FINISHED;
313         }
314         else {
315                 MaskSplinePointUW *uw;
316
317                 if (ED_mask_feather_find_nearest(C, mask, co, threshold, &masklay, &spline, &point, &uw, NULL)) {
318
319                         if (extend) {
320                                 masklay->act_spline = spline;
321                                 masklay->act_point = point;
322
323                                 if (uw) uw->flag |= SELECT;
324                         }
325                         else if (deselect) {
326                                 if (uw) uw->flag &= ~SELECT;
327                         }
328                         else {
329                                 masklay->act_spline = spline;
330                                 masklay->act_point = point;
331
332                                 if (uw) {
333                                         if (!(uw->flag & SELECT)) {
334                                                 uw->flag |= SELECT;
335                                         }
336                                         else if (toggle) {
337                                                 uw->flag &= ~SELECT;
338                                         }
339                                 }
340                         }
341
342                         ED_mask_select_flush_all(mask);
343
344                         WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
345
346                         return OPERATOR_FINISHED;
347                 }
348         }
349
350         return OPERATOR_PASS_THROUGH;
351 }
352
353 static int select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
354 {
355         ScrArea *sa = CTX_wm_area(C);
356         ARegion *ar = CTX_wm_region(C);
357
358         float co[2];
359
360         ED_mask_mouse_pos(sa, ar, event->mval, co);
361
362         RNA_float_set_array(op->ptr, "location", co);
363
364         return select_exec(C, op);
365 }
366
367 void MASK_OT_select(wmOperatorType *ot)
368 {
369         /* identifiers */
370         ot->name = "Select";
371         ot->description = "Select spline points";
372         ot->idname = "MASK_OT_select";
373
374         /* api callbacks */
375         ot->exec = select_exec;
376         ot->invoke = select_invoke;
377         ot->poll = ED_maskedit_mask_poll;
378
379         /* flags */
380         ot->flag = OPTYPE_UNDO;
381
382         /* properties */
383         WM_operator_properties_mouse_select(ot);
384
385         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
386                              "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
387 }
388
389
390
391 /********************** box select operator *********************/
392
393 static int box_select_exec(bContext *C, wmOperator *op)
394 {
395         ScrArea *sa = CTX_wm_area(C);
396         ARegion *ar = CTX_wm_region(C);
397
398         Mask *mask = CTX_data_edit_mask(C);
399         MaskLayer *masklay;
400         int i;
401
402         rcti rect;
403         rctf rectf;
404         bool changed = false;
405         const bool select = !RNA_boolean_get(op->ptr, "deselect");
406         const bool extend = RNA_boolean_get(op->ptr, "extend");
407
408         /* get rectangle from operator */
409         WM_operator_properties_border_to_rcti(op, &rect);
410
411         ED_mask_point_pos(sa, ar, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
412         ED_mask_point_pos(sa, ar, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
413
414         /* do actual selection */
415         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
416                 MaskSpline *spline;
417
418                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
419                         continue;
420                 }
421
422                 for (spline = masklay->splines.first; spline; spline = spline->next) {
423                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
424
425                         for (i = 0; i < spline->tot_point; i++) {
426                                 MaskSplinePoint *point = &spline->points[i];
427                                 MaskSplinePoint *point_deform = &points_array[i];
428
429                                 /* TODO: handles? */
430                                 /* TODO: uw? */
431
432                                 if (BLI_rctf_isect_pt_v(&rectf, point_deform->bezt.vec[1])) {
433                                         BKE_mask_point_select_set(point, select);
434                                         BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select);
435                                 }
436                                 else if (!extend) {
437                                         BKE_mask_point_select_set(point, false);
438                                         BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, false);
439                                 }
440
441                                 changed = true;
442                         }
443                 }
444         }
445
446         if (changed) {
447                 ED_mask_select_flush_all(mask);
448
449                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
450
451                 return OPERATOR_FINISHED;
452         }
453
454         return OPERATOR_CANCELLED;
455 }
456
457 void MASK_OT_select_box(wmOperatorType *ot)
458 {
459         /* identifiers */
460         ot->name = "Box Select";
461         ot->description = "Select curve points using box selection";
462         ot->idname = "MASK_OT_select_box";
463
464         /* api callbacks */
465         ot->invoke = WM_gesture_box_invoke;
466         ot->exec = box_select_exec;
467         ot->modal = WM_gesture_box_modal;
468         ot->poll = ED_maskedit_mask_poll;
469
470         /* flags */
471         ot->flag = OPTYPE_UNDO;
472
473         /* properties */
474         WM_operator_properties_gesture_box_select(ot);
475 }
476
477 static bool do_lasso_select_mask(bContext *C, const int mcords[][2], short moves, bool select, bool extend)
478 {
479         ScrArea *sa = CTX_wm_area(C);
480         ARegion *ar = CTX_wm_region(C);
481
482         Mask *mask = CTX_data_edit_mask(C);
483         MaskLayer *masklay;
484         int i;
485
486         rcti rect;
487         bool changed = false;
488
489         /* get rectangle from operator */
490         BLI_lasso_boundbox(&rect, mcords, moves);
491
492         /* do actual selection */
493         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
494                 MaskSpline *spline;
495
496                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
497                         continue;
498                 }
499
500                 for (spline = masklay->splines.first; spline; spline = spline->next) {
501                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
502
503                         for (i = 0; i < spline->tot_point; i++) {
504                                 MaskSplinePoint *point = &spline->points[i];
505                                 MaskSplinePoint *point_deform = &points_array[i];
506
507                                 /* TODO: handles? */
508                                 /* TODO: uw? */
509
510                                 if (MASKPOINT_ISSEL_ANY(point) && select && extend) {
511                                         continue;
512                                 }
513
514                                 float screen_co[2];
515
516                                 /* point in screen coords */
517                                 ED_mask_point_pos__reverse(sa, ar,
518                                                            point_deform->bezt.vec[1][0], point_deform->bezt.vec[1][1],
519                                                            &screen_co[0], &screen_co[1]);
520
521                                 if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
522                                     BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX))
523                                 {
524                                         BKE_mask_point_select_set(point, select);
525                                         BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select);
526                                         changed = true;
527                                 }
528                                 else if (select && !extend) {
529                                         BKE_mask_point_select_set(point, false);
530                                         BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, false);
531                                         changed = true;
532                                 }
533                         }
534                 }
535         }
536
537         if (changed) {
538                 ED_mask_select_flush_all(mask);
539
540                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
541         }
542
543         return changed;
544 }
545
546 static int clip_lasso_select_exec(bContext *C, wmOperator *op)
547 {
548         int mcords_tot;
549         const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
550
551         if (mcords) {
552                 const bool select = !RNA_boolean_get(op->ptr, "deselect");
553                 const bool extend = RNA_boolean_get(op->ptr, "extend");
554                 do_lasso_select_mask(C, mcords, mcords_tot, select, extend);
555
556                 MEM_freeN((void *)mcords);
557
558                 return OPERATOR_FINISHED;
559         }
560         return OPERATOR_PASS_THROUGH;
561 }
562
563 void MASK_OT_select_lasso(wmOperatorType *ot)
564 {
565         /* identifiers */
566         ot->name = "Lasso Select";
567         ot->description = "Select curve points using lasso selection";
568         ot->idname = "MASK_OT_select_lasso";
569
570         /* api callbacks */
571         ot->invoke = WM_gesture_lasso_invoke;
572         ot->modal = WM_gesture_lasso_modal;
573         ot->exec = clip_lasso_select_exec;
574         ot->poll = ED_maskedit_mask_poll;
575         ot->cancel = WM_gesture_lasso_cancel;
576
577         /* flags */
578         ot->flag = OPTYPE_UNDO;
579
580         /* properties */
581         WM_operator_properties_gesture_lasso_select(ot);
582 }
583
584 /********************** circle select operator *********************/
585
586 static int mask_spline_point_inside_ellipse(BezTriple *bezt, const float offset[2], const float ellipse[2])
587 {
588         /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
589         float x, y;
590
591         x = (bezt->vec[1][0] - offset[0]) * ellipse[0];
592         y = (bezt->vec[1][1] - offset[1]) * ellipse[1];
593
594         return x * x + y * y < 1.0f;
595 }
596
597 static int circle_select_exec(bContext *C, wmOperator *op)
598 {
599         ScrArea *sa = CTX_wm_area(C);
600         ARegion *ar = CTX_wm_region(C);
601
602         Mask *mask = CTX_data_edit_mask(C);
603         MaskLayer *masklay;
604         int i;
605
606         float zoomx, zoomy, offset[2], ellipse[2];
607         int width, height;
608         bool changed = false;
609
610         /* get operator properties */
611         const int x = RNA_int_get(op->ptr, "x");
612         const int y = RNA_int_get(op->ptr, "y");
613         const int radius = RNA_int_get(op->ptr, "radius");
614
615         const bool select = !RNA_boolean_get(op->ptr, "deselect");
616
617         /* compute ellipse and position in unified coordinates */
618         ED_mask_get_size(sa, &width, &height);
619         ED_mask_zoom(sa, ar, &zoomx, &zoomy);
620         width = height = max_ii(width, height);
621
622         ellipse[0] = width * zoomx / radius;
623         ellipse[1] = height * zoomy / radius;
624
625         ED_mask_point_pos(sa, ar, x, y, &offset[0], &offset[1]);
626
627         /* do actual selection */
628         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
629                 MaskSpline *spline;
630
631                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
632                         continue;
633                 }
634
635                 for (spline = masklay->splines.first; spline; spline = spline->next) {
636                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
637
638                         for (i = 0; i < spline->tot_point; i++) {
639                                 MaskSplinePoint *point = &spline->points[i];
640                                 MaskSplinePoint *point_deform = &points_array[i];
641
642                                 if (mask_spline_point_inside_ellipse(&point_deform->bezt, offset, ellipse)) {
643                                         BKE_mask_point_select_set(point, select);
644                                         BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select);
645
646                                         changed = true;
647                                 }
648                         }
649                 }
650         }
651
652         if (changed) {
653                 ED_mask_select_flush_all(mask);
654
655                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
656
657                 return OPERATOR_FINISHED;
658         }
659
660         return OPERATOR_CANCELLED;
661 }
662
663 void MASK_OT_select_circle(wmOperatorType *ot)
664 {
665         /* identifiers */
666         ot->name = "Circle Select";
667         ot->description = "Select curve points using circle selection";
668         ot->idname = "MASK_OT_select_circle";
669
670         /* api callbacks */
671         ot->invoke = WM_gesture_circle_invoke;
672         ot->modal = WM_gesture_circle_modal;
673         ot->exec = circle_select_exec;
674         ot->poll = ED_maskedit_mask_poll;
675
676         /* flags */
677         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
678
679         /* properties */
680         WM_operator_properties_gesture_circle_select(ot);
681 }
682
683 static int mask_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
684 {
685         ScrArea *sa = CTX_wm_area(C);
686         ARegion *ar = CTX_wm_region(C);
687
688         Mask *mask = CTX_data_edit_mask(C);
689         MaskLayer *masklay;
690         MaskSpline *spline;
691         MaskSplinePoint *point = NULL;
692         float co[2];
693         bool do_select = !RNA_boolean_get(op->ptr, "deselect");
694         const float threshold = 19;
695         bool changed = false;
696
697         ED_mask_mouse_pos(sa, ar, event->mval, co);
698
699         point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, NULL, NULL);
700
701         if (point) {
702                 ED_mask_spline_select_set(spline, do_select);
703                 masklay->act_spline = spline;
704                 masklay->act_point = point;
705
706                 changed = true;
707         }
708
709         if (changed) {
710                 ED_mask_select_flush_all(mask);
711
712                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
713
714                 return OPERATOR_FINISHED;
715         }
716
717         return OPERATOR_CANCELLED;
718 }
719
720 void MASK_OT_select_linked_pick(wmOperatorType *ot)
721 {
722         /* identifiers */
723         ot->name = "Select Linked";
724         ot->idname = "MASK_OT_select_linked_pick";
725         ot->description = "(De)select all points linked to the curve under the mouse cursor";
726
727         /* api callbacks */
728         ot->invoke = mask_select_linked_pick_invoke;
729         ot->poll = ED_maskedit_mask_poll;
730
731         /* flags */
732         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
733
734         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
735 }
736
737 static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
738 {
739         Mask *mask = CTX_data_edit_mask(C);
740         MaskLayer *masklay;
741
742         bool changed = false;
743
744         /* do actual selection */
745         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
746                 MaskSpline *spline;
747
748                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
749                         continue;
750                 }
751
752                 for (spline = masklay->splines.first; spline; spline = spline->next) {
753                         if (ED_mask_spline_select_check(spline)) {
754                                 ED_mask_spline_select_set(spline, true);
755                                 changed = true;
756                         }
757                 }
758         }
759
760         if (changed) {
761                 ED_mask_select_flush_all(mask);
762
763                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
764
765                 return OPERATOR_FINISHED;
766         }
767
768         return OPERATOR_CANCELLED;
769 }
770
771 void MASK_OT_select_linked(wmOperatorType *ot)
772 {
773         /* identifiers */
774         ot->name = "Select Linked All";
775         ot->idname = "MASK_OT_select_linked";
776         ot->description = "Select all curve points linked to already selected ones";
777
778         /* api callbacks */
779         ot->exec = mask_select_linked_exec;
780         ot->poll = ED_maskedit_mask_poll;
781
782         /* flags */
783         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
784 }
785
786 /**************** Select more/less **************/
787
788 static int mask_select_more_less(bContext *C, bool more)
789 {
790         Mask *mask = CTX_data_edit_mask(C);
791         MaskLayer *masklay;
792
793         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
794                 MaskSpline *spline;
795
796                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
797                         continue;
798                 }
799
800                 for (spline = masklay->splines.first; spline; spline = spline->next) {
801                         const bool cyclic = (spline->flag & MASK_SPLINE_CYCLIC) != 0;
802                         bool start_sel, end_sel, prev_sel, cur_sel;
803                         int i;
804
805                         /* reselect point if any handle is selected to make the result more predictable */
806                         for (i = 0; i < spline->tot_point; i++) {
807                                 BKE_mask_point_select_set(spline->points + i, MASKPOINT_ISSEL_ANY(spline->points + i));
808                         }
809
810                         /* select more/less does not affect empty/single point splines */
811                         if (spline->tot_point < 2) {
812                                 continue;
813                         }
814
815                         if (cyclic) {
816                                 start_sel = !!MASKPOINT_ISSEL_KNOT(spline->points);
817                                 end_sel = !!MASKPOINT_ISSEL_KNOT(&spline->points[spline->tot_point - 1]);
818                         }
819                         else {
820                                 start_sel = false;
821                                 end_sel = false;
822                         }
823
824                         for (i = 0; i < spline->tot_point; i++) {
825                                 if (i == 0 && !cyclic) {
826                                         continue;
827                                 }
828
829                                 prev_sel = (i > 0) ? !!MASKPOINT_ISSEL_KNOT(&spline->points[i - 1]) : end_sel;
830                                 cur_sel = !!MASKPOINT_ISSEL_KNOT(&spline->points[i]);
831
832                                 if (cur_sel != more) {
833                                         if (prev_sel == more) {
834                                                 BKE_mask_point_select_set(&spline->points[i], more);
835                                         }
836                                         i++;
837                                 }
838                         }
839
840                         for (i = spline->tot_point - 1; i >= 0; i--) {
841                                 if (i == spline->tot_point - 1 && !cyclic) {
842                                         continue;
843                                 }
844
845                                 prev_sel = (i < spline->tot_point - 1) ? !!MASKPOINT_ISSEL_KNOT(&spline->points[i + 1]) : start_sel;
846                                 cur_sel = !!MASKPOINT_ISSEL_KNOT(&spline->points[i]);
847
848                                 if (cur_sel != more) {
849                                         if (prev_sel == more) {
850                                                 BKE_mask_point_select_set(&spline->points[i], more);
851                                         }
852                                         i--;
853                                 }
854                         }
855                 }
856         }
857
858         WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
859
860         return OPERATOR_FINISHED;
861 }
862
863 static int mask_select_more_exec(bContext *C, wmOperator *UNUSED(op))
864 {
865         return mask_select_more_less(C, true);
866 }
867
868 void MASK_OT_select_more(wmOperatorType *ot)
869 {
870         /* identifiers */
871         ot->name = "Select More";
872         ot->idname = "MASK_OT_select_more";
873         ot->description = "Select more spline points connected to initial selection";
874
875         /* api callbacks */
876         ot->exec = mask_select_more_exec;
877         ot->poll = ED_maskedit_mask_poll;
878
879         /* flags */
880         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
881 }
882
883 static int mask_select_less_exec(bContext *C, wmOperator *UNUSED(op))
884 {
885         return mask_select_more_less(C, false);
886 }
887
888 void MASK_OT_select_less(wmOperatorType *ot)
889 {
890         /* identifiers */
891         ot->name = "Select Less";
892         ot->idname = "MASK_OT_select_less";
893         ot->description = "Deselect spline points at the boundary of each selection region";
894
895         /* api callbacks */
896         ot->exec = mask_select_less_exec;
897         ot->poll = ED_maskedit_mask_poll;
898
899         /* flags */
900         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
901 }