abbreviate mask-editing to mask-edit
[blender-staging.git] / source / blender / editors / mask / mask_ops.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_ops.c
29  *  \ingroup edmask
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_listbase.h"
35 #include "BLI_math.h"
36
37 #include "BKE_context.h"
38 #include "BKE_depsgraph.h"
39 #include "BKE_mask.h"
40
41 #include "DNA_scene_types.h"
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_mask.h"
50 #include "ED_clip.h"
51 #include "ED_keyframing.h"
52
53 #include "RNA_access.h"
54 #include "RNA_define.h"
55
56 #include "mask_intern.h"  /* own include */
57
58 /******************** utility functions *********************/
59
60 MaskSplinePoint *ED_mask_point_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold,
61                                             MaskLayer **masklay_r, MaskSpline **spline_r, int *is_handle_r,
62                                             float *score)
63 {
64         MaskLayer *masklay;
65         MaskLayer *point_masklay = NULL;
66         MaskSpline *point_spline = NULL;
67         MaskSplinePoint *point = NULL;
68         float co[2], aspx, aspy;
69         float len = FLT_MAX, scalex, scaley;
70         int is_handle = FALSE, width, height;
71
72         ED_mask_size(C, &width, &height);
73         ED_mask_aspect(C, &aspx, &aspy);
74         ED_mask_pixelspace_factor(C, &scalex, &scaley);
75
76         co[0] = normal_co[0] * scalex;
77         co[1] = normal_co[1] * scaley;
78
79         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
80                 MaskSpline *spline;
81
82                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
83                         continue;
84                 }
85
86                 for (spline = masklay->splines.first; spline; spline = spline->next) {
87                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
88
89                         int i;
90
91                         for (i = 0; i < spline->tot_point; i++) {
92                                 MaskSplinePoint *cur_point = &spline->points[i];
93                                 MaskSplinePoint *cur_point_deform = &points_array[i];
94                                 float cur_len, vec[2], handle[2];
95
96                                 vec[0] = cur_point_deform->bezt.vec[1][0] * scalex;
97                                 vec[1] = cur_point_deform->bezt.vec[1][1] * scaley;
98
99                                 if (BKE_mask_point_has_handle(cur_point)) {
100                                         BKE_mask_point_handle(cur_point_deform, handle);
101                                         handle[0] *= scalex;
102                                         handle[1] *= scaley;
103
104                                         cur_len = len_v2v2(co, handle);
105
106                                         if (cur_len < len) {
107                                                 point_masklay = masklay;
108                                                 point_spline = spline;
109                                                 point = cur_point;
110                                                 len = cur_len;
111                                                 is_handle = TRUE;
112                                         }
113                                 }
114
115                                 cur_len = len_v2v2(co, vec);
116
117                                 if (cur_len < len) {
118                                         point_spline = spline;
119                                         point_masklay = masklay;
120                                         point = cur_point;
121                                         len = cur_len;
122                                         is_handle = FALSE;
123                                 }
124                         }
125                 }
126         }
127
128         if (len < threshold) {
129                 if (masklay_r)
130                         *masklay_r = point_masklay;
131
132                 if (spline_r)
133                         *spline_r = point_spline;
134
135                 if (is_handle_r)
136                         *is_handle_r = is_handle;
137
138                 if (score)
139                         *score = len;
140
141                 return point;
142         }
143
144         if (masklay_r)
145                 *masklay_r = NULL;
146
147         if (spline_r)
148                 *spline_r = NULL;
149
150         if (is_handle_r)
151                 *is_handle_r = FALSE;
152
153         return NULL;
154 }
155
156 int ED_mask_feather_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold,
157                                  MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
158                                  MaskSplinePointUW **uw_r, float *score)
159 {
160         MaskLayer *masklay, *point_masklay = NULL;
161         MaskSpline *point_spline = NULL;
162         MaskSplinePoint *point = NULL;
163         MaskSplinePointUW *uw = NULL;
164         float len = FLT_MAX, co[2];
165         float scalex, scaley, aspx, aspy;
166         int width, height;
167
168         ED_mask_size(C, &width, &height);
169         ED_mask_aspect(C, &aspx, &aspy);
170         ED_mask_pixelspace_factor(C, &scalex, &scaley);
171
172         co[0] = normal_co[0] * scalex;
173         co[1] = normal_co[1] * scaley;
174
175         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
176                 MaskSpline *spline;
177
178                 for (spline = masklay->splines.first; spline; spline = spline->next) {
179                         //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
180
181                         int i, tot_feather_point;
182                         float (*feather_points)[2], (*fp)[2];
183
184                         if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
185                                 continue;
186                         }
187
188                         feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
189
190                         for (i = 0; i < spline->tot_point; i++) {
191                                 int j;
192                                 MaskSplinePoint *cur_point = &spline->points[i];
193
194                                 for (j = 0; j < cur_point->tot_uw + 1; j++) {
195                                         float cur_len, vec[2];
196
197                                         vec[0] = (*fp)[0] * scalex;
198                                         vec[1] = (*fp)[1] * scaley;
199
200                                         cur_len = len_v2v2(vec, co);
201
202                                         if (point == NULL || cur_len < len) {
203                                                 if (j == 0)
204                                                         uw = NULL;
205                                                 else
206                                                         uw = &cur_point->uw[j - 1];
207
208                                                 point_masklay = masklay;
209                                                 point_spline = spline;
210                                                 point = cur_point;
211                                                 len = cur_len;
212                                         }
213
214                                         fp++;
215                                 }
216                         }
217
218                         MEM_freeN(feather_points);
219                 }
220         }
221
222         if (len < threshold) {
223                 if (masklay_r)
224                         *masklay_r = point_masklay;
225
226                 if (spline_r)
227                         *spline_r = point_spline;
228
229                 if (point_r)
230                         *point_r = point;
231
232                 if (uw_r)
233                         *uw_r = uw;
234
235                 if (score)
236                         *score = len;
237
238                 return TRUE;
239         }
240
241         if (masklay_r)
242                 *masklay_r = NULL;
243
244         if (spline_r)
245                 *spline_r = NULL;
246
247         if (point_r)
248                 *point_r = NULL;
249
250         return FALSE;
251 }
252
253
254 /******************** create new mask *********************/
255
256 static int mask_new_exec(bContext *C, wmOperator *op)
257 {
258         SpaceClip *sc = CTX_wm_space_clip(C);
259         Mask *mask;
260         char name[MAX_ID_NAME - 2];
261
262         RNA_string_get(op->ptr, "name", name);
263
264         mask = BKE_mask_new(name);
265
266         if (sc)
267                 ED_space_clip_set_mask(C, sc, mask);
268
269         return OPERATOR_FINISHED;
270 }
271
272 void MASK_OT_new(wmOperatorType *ot)
273 {
274         /* identifiers */
275         ot->name = "New Mask";
276         ot->description = "Create new mask";
277         ot->idname = "MASK_OT_new";
278
279         /* flags */
280         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
281
282         /* api callbacks */
283         ot->exec = mask_new_exec;
284         ot->poll = ED_operator_mask;
285
286         /* properties */
287         RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask");
288 }
289
290 /******************** create new masklay *********************/
291
292 static int masklay_new_exec(bContext *C, wmOperator *op)
293 {
294         Mask *mask = CTX_data_edit_mask(C);
295         char name[MAX_ID_NAME - 2];
296
297         RNA_string_get(op->ptr, "name", name);
298
299         BKE_mask_layer_new(mask, name);
300         mask->masklay_act = mask->masklay_tot - 1;
301
302         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
303
304         return OPERATOR_FINISHED;
305 }
306
307 void MASK_OT_layer_new(wmOperatorType *ot)
308 {
309         /* identifiers */
310         ot->name = "Add Mask Layer";
311         ot->description = "Add new mask layer for masking";
312         ot->idname = "MASK_OT_layer_new";
313
314         /* api callbacks */
315         ot->exec = masklay_new_exec;
316         ot->poll = ED_maskedit_poll;
317
318         /* flags */
319         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
320
321         /* properties */
322         RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask layer");
323 }
324
325 /******************** remove mask layer *********************/
326
327 static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
328 {
329         Mask *mask = CTX_data_edit_mask(C);
330         MaskLayer *masklay = BKE_mask_layer_active(mask);
331
332         if (masklay) {
333                 BKE_mask_layer_remove(mask, masklay);
334
335                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
336         }
337
338         return OPERATOR_FINISHED;
339 }
340
341 void MASK_OT_layer_remove(wmOperatorType *ot)
342 {
343         /* identifiers */
344         ot->name = "Remove Mask Layer";
345         ot->description = "Remove mask layer";
346         ot->idname = "MASK_OT_layer_remove";
347
348         /* api callbacks */
349         ot->exec = masklay_remove_exec;
350         ot->poll = ED_maskedit_poll;
351
352         /* flags */
353         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
354 }
355
356 /******************** slide *********************/
357
358 #define SLIDE_ACTION_NONE       0
359 #define SLIDE_ACTION_POINT      1
360 #define SLIDE_ACTION_HANDLE     2
361 #define SLIDE_ACTION_FEATHER    3
362
363 typedef struct SlidePointData {
364         int action;
365
366         float co[2];
367         float vec[3][3];
368
369         Mask *mask;
370         MaskLayer *masklay;
371         MaskSpline *spline, *orig_spline;
372         MaskSplinePoint *point;
373         MaskSplinePointUW *uw;
374         float handle[2], no[2], feather[2];
375         int width, height;
376         float weight;
377
378         short curvature_only, accurate;
379         short initial_feather, overall_feather;
380 } SlidePointData;
381
382 static int slide_point_check_initial_feather(MaskSpline *spline)
383 {
384         int i;
385
386         for (i = 0; i < spline->tot_point; i++) {
387                 MaskSplinePoint *point = &spline->points[i];
388                 int j;
389
390                 if (point->bezt.weight != 0.0f)
391                         return FALSE;
392
393                 for (j = 0; j < point->tot_uw; j++) {
394                         if (point->uw[j].w != 0.0f)
395                                 return FALSE;
396                 }
397         }
398
399         return TRUE;
400 }
401
402 static void *slide_point_customdata(bContext *C, wmOperator *op, wmEvent *event)
403 {
404         Mask *mask = CTX_data_edit_mask(C);
405         SlidePointData *customdata = NULL;
406         MaskLayer *masklay, *cv_masklay, *feather_masklay;
407         MaskSpline *spline, *cv_spline, *feather_spline;
408         MaskSplinePoint *point, *cv_point, *feather_point;
409         MaskSplinePointUW *uw = NULL;
410         int is_handle = FALSE, width, height, action = SLIDE_ACTION_NONE;
411         int slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
412         float co[2], cv_score, feather_score;
413         const float threshold = 19;
414
415         ED_mask_mouse_pos(C, event, co);
416         ED_mask_size(C, &width, &height);
417
418         cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &is_handle, &cv_score);
419
420         if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
421                 if (slide_feather || !cv_point || feather_score < cv_score) {
422                         action = SLIDE_ACTION_FEATHER;
423
424                         masklay = feather_masklay;
425                         spline = feather_spline;
426                         point = feather_point;
427                 }
428         }
429
430         if (cv_point && action == SLIDE_ACTION_NONE) {
431                 if (is_handle)
432                         action = SLIDE_ACTION_HANDLE;
433                 else
434                         action = SLIDE_ACTION_POINT;
435
436                 masklay = cv_masklay;
437                 spline = cv_spline;
438                 point = cv_point;
439         }
440
441         if (action != SLIDE_ACTION_NONE) {
442                 customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
443
444                 customdata->mask = mask;
445                 customdata->masklay = masklay;
446                 customdata->spline = spline;
447                 customdata->point = point;
448                 customdata->width = width;
449                 customdata->height = height;
450                 customdata->action = action;
451                 customdata->uw = uw;
452
453                 if (uw) {
454                         float co[2];
455
456                         customdata->weight = point->bezt.weight;
457
458                         customdata->weight = uw->w;
459                         BKE_mask_point_segment_co(spline, point, uw->u, co);
460                         BKE_mask_point_normal(spline, point, uw->u, customdata->no);
461
462                         customdata->feather[0] = co[0] + customdata->no[0] * uw->w;
463                         customdata->feather[1] = co[1] + customdata->no[1] * uw->w;
464                 }
465                 else {
466                         BezTriple *bezt = &point->bezt;
467
468                         BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
469
470                         customdata->feather[0] = bezt->vec[1][0] + customdata->no[0] * bezt->weight;
471                         customdata->feather[1] = bezt->vec[1][1] + customdata->no[1] * bezt->weight;
472
473                         customdata->weight = bezt->weight;
474                 }
475
476                 if (customdata->action == SLIDE_ACTION_FEATHER)
477                         customdata->initial_feather = slide_point_check_initial_feather(spline);
478
479                 copy_m3_m3(customdata->vec, point->bezt.vec);
480                 if (BKE_mask_point_has_handle(point))
481                         BKE_mask_point_handle(point, customdata->handle);
482                 ED_mask_mouse_pos(C, event, customdata->co);
483         }
484
485         return customdata;
486 }
487
488 static int slide_point_invoke(bContext *C, wmOperator *op, wmEvent *event)
489 {
490         SlidePointData *slidedata = slide_point_customdata(C, op, event);
491
492         if (slidedata) {
493                 Mask *mask = CTX_data_edit_mask(C);
494
495                 op->customdata = slidedata;
496
497                 WM_event_add_modal_handler(C, op);
498
499                 if (slidedata->uw) {
500                         if ((slidedata->uw->flag & SELECT) == 0) {
501                                 ED_mask_select_toggle_all(mask, SEL_DESELECT);
502
503                                 slidedata->uw->flag |= SELECT;
504
505                                 ED_mask_select_flush_all(mask);
506                         }
507                 }
508                 else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) {
509                         ED_mask_select_toggle_all(mask, SEL_DESELECT);
510
511                         BKE_mask_point_select_set(slidedata->point, TRUE);
512
513                         ED_mask_select_flush_all(mask);
514                 }
515
516                 slidedata->masklay->act_spline = slidedata->spline;
517                 slidedata->masklay->act_point = slidedata->point;
518
519                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
520
521                 return OPERATOR_RUNNING_MODAL;
522         }
523
524         return OPERATOR_PASS_THROUGH;
525 }
526
527 static void slide_point_delta_all_feather(SlidePointData *data, float delta)
528 {
529         int i;
530
531         for (i = 0; i < data->spline->tot_point; i++) {
532                 MaskSplinePoint *point = &data->spline->points[i];
533                 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
534                 int j;
535
536                 point->bezt.weight = orig_point->bezt.weight + delta;
537                 if (point->bezt.weight < 0.0f)
538                         point->bezt.weight = 0.0f;
539
540                 for (j = 0; j < point->tot_uw; j++) {
541                         point->uw[j].w = orig_point->uw[j].w + delta;
542                         if (point->uw[j].w < 0.0f)
543                                 point->uw[j].w = 0.0f;
544                 }
545         }
546 }
547
548 static void slide_point_restore_spline(SlidePointData *data)
549 {
550         int i;
551
552         for (i = 0; i < data->spline->tot_point; i++) {
553                 MaskSplinePoint *point = &data->spline->points[i];
554                 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
555                 int j;
556
557                 point->bezt = orig_point->bezt;
558
559                 for (j = 0; j < point->tot_uw; j++)
560                         point->uw[j] = orig_point->uw[j];
561         }
562 }
563
564 static void cancel_slide_point(SlidePointData *data)
565 {
566         /* cancel sliding */
567
568         if (data->orig_spline) {
569                 slide_point_restore_spline(data);
570         }
571         else {
572                 if (data->action == SLIDE_ACTION_FEATHER) {
573                         if (data->uw)
574                                 data->uw->w = data->weight;
575                         else
576                                 data->point->bezt.weight = data->weight;
577                 }
578                 else {
579                         copy_m3_m3(data->point->bezt.vec, data->vec);
580                 }
581         }
582 }
583
584 static void free_slide_point_data(SlidePointData *data)
585 {
586         if (data->orig_spline)
587                 BKE_mask_spline_free(data->orig_spline);
588
589         MEM_freeN(data);
590 }
591
592 static int slide_point_modal(bContext *C, wmOperator *op, wmEvent *event)
593 {
594         SlidePointData *data = (SlidePointData *)op->customdata;
595         BezTriple *bezt = &data->point->bezt;
596         float co[2], dco[2];
597
598         switch (event->type) {
599                 case LEFTCTRLKEY:
600                 case RIGHTCTRLKEY:
601                 case LEFTSHIFTKEY:
602                 case RIGHTSHIFTKEY:
603                         if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY)) {
604                                 if (data->action == SLIDE_ACTION_FEATHER)
605                                         data->overall_feather = event->val == KM_PRESS;
606                                 else
607                                         data->curvature_only = event->val == KM_PRESS;
608                         }
609
610                         if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
611                                 data->accurate = event->val == KM_PRESS;
612
613                 /* no break! update CV position */
614
615                 case MOUSEMOVE:
616                         ED_mask_mouse_pos(C, event, co);
617                         sub_v2_v2v2(dco, co, data->co);
618
619                         if (data->action == SLIDE_ACTION_HANDLE) {
620                                 float delta[2], offco[2];
621
622                                 sub_v2_v2v2(delta, data->handle, data->co);
623
624                                 sub_v2_v2v2(offco, co, data->co);
625                                 if (data->accurate)
626                                         mul_v2_fl(offco, 0.2f);
627                                 add_v2_v2(offco, data->co);
628                                 add_v2_v2(offco, delta);
629
630                                 BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->handle, data->vec);
631                         }
632                         else if (data->action == SLIDE_ACTION_POINT) {
633                                 float delta[2];
634
635                                 copy_v2_v2(delta, dco);
636                                 if (data->accurate)
637                                         mul_v2_fl(delta, 0.2f);
638
639                                 add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
640                                 add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
641                                 add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
642                         }
643                         else if (data->action == SLIDE_ACTION_FEATHER) {
644                                 float vec[2], no[2], p[2], c[2], w, offco[2];
645                                 float *weight = NULL;
646                                 int overall_feather = data->overall_feather || data->initial_feather;
647
648                                 add_v2_v2v2(offco, data->feather, dco);
649
650                                 if (data->uw) {
651                                         float u = BKE_mask_spline_project_co(data->spline, data->point, data->uw->u, offco);
652
653                                         if (u > 0.0f && u < 1.0f) {
654                                                 data->uw->u = u;
655
656                                                 data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
657                                                 weight = &data->uw->w;
658                                                 BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
659                                                 BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
660                                         }
661                                 }
662                                 else {
663                                         weight = &bezt->weight;
664                                         copy_v2_v2(no, data->no);
665                                         copy_v2_v2(p, bezt->vec[1]);
666                                 }
667
668                                 if (weight) {
669                                         sub_v2_v2v2(c, offco, p);
670                                         project_v2_v2v2(vec, c, no);
671
672                                         w = len_v2(vec);
673
674                                         if (overall_feather) {
675                                                 float delta;
676
677                                                 if (dot_v2v2(no, vec) <= 0.0f)
678                                                         w = -w;
679
680                                                 delta = w - data->weight;
681
682                                                 if (data->orig_spline == NULL) {
683                                                         /* restore weight for currently sliding point, so orig_spline would be created
684                                                          * with original weights used
685                                                          */
686                                                         *weight = data->weight;
687
688                                                         data->orig_spline = BKE_mask_spline_copy(data->spline);
689                                                 }
690
691                                                 slide_point_delta_all_feather(data, delta);
692                                         }
693                                         else {
694                                                 if (dot_v2v2(no, vec) <= 0.0f)
695                                                         w = 0.0f;
696
697                                                 if (data->orig_spline) {
698                                                         /* restore possible overall feather changes */
699                                                         slide_point_restore_spline(data);
700
701                                                         BKE_mask_spline_free(data->orig_spline);
702                                                         data->orig_spline = NULL;
703                                                 }
704
705                                                 *weight = w;
706                                         }
707                                 }
708                         }
709
710                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
711                         DAG_id_tag_update(&data->mask->id, 0);
712
713                         break;
714
715                 case LEFTMOUSE:
716                         if (event->val == KM_RELEASE) {
717                                 Scene *scene = CTX_data_scene(C);
718
719                                 free_slide_point_data(op->customdata);
720
721                                 if (IS_AUTOKEY_ON(scene)) {
722                                         ED_mask_layer_shape_auto_key_all(data->mask, CFRA);
723                                 }
724
725                                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
726                                 DAG_id_tag_update(&data->mask->id, 0);
727
728                                 return OPERATOR_FINISHED;
729                         }
730
731                         break;
732
733                 case ESCKEY:
734                         cancel_slide_point(op->customdata);
735
736                         free_slide_point_data(op->customdata);
737
738                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
739                         DAG_id_tag_update(&data->mask->id, 0);
740
741                         return OPERATOR_CANCELLED;
742         }
743
744         return OPERATOR_RUNNING_MODAL;
745 }
746
747 void MASK_OT_slide_point(wmOperatorType *ot)
748 {
749         /* identifiers */
750         ot->name = "Slide Point";
751         ot->description = "Slide control points";
752         ot->idname = "MASK_OT_slide_point";
753
754         /* api callbacks */
755         ot->invoke = slide_point_invoke;
756         ot->modal = slide_point_modal;
757         ot->poll = ED_maskedit_mask_poll;
758
759         /* flags */
760         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
761
762         RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide slide feather instead of vertex");
763 }
764
765 /******************** toggle cyclic *********************/
766
767 static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
768 {
769         Mask *mask = CTX_data_edit_mask(C);
770         MaskLayer *masklay;
771
772         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
773                 MaskSpline *spline;
774
775                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
776                         continue;
777                 }
778
779                 for (spline = masklay->splines.first; spline; spline = spline->next) {
780                         if (ED_mask_spline_select_check(spline)) {
781                                 spline->flag ^= MASK_SPLINE_CYCLIC;
782                         }
783                 }
784         }
785
786         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
787
788         return OPERATOR_FINISHED;
789 }
790
791 void MASK_OT_cyclic_toggle(wmOperatorType *ot)
792 {
793         /* identifiers */
794         ot->name = "Toggle Cyclic";
795         ot->description = "Toggle cyclic for selected splines";
796         ot->idname = "MASK_OT_cyclic_toggle";
797
798         /* api callbacks */
799         ot->exec = cyclic_toggle_exec;
800         ot->poll = ED_maskedit_mask_poll;
801
802         /* flags */
803         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
804 }
805
806 /******************** delete *********************/
807
808 static void delete_feather_points(MaskSplinePoint *point)
809 {
810         int i, count = 0;
811
812         if (!point->tot_uw)
813                 return;
814
815         for (i = 0; i < point->tot_uw; i++) {
816                 if ((point->uw[i].flag & SELECT) == 0)
817                         count++;
818         }
819
820         if (count == 0) {
821                 MEM_freeN(point->uw);
822                 point->uw = NULL;
823                 point->tot_uw = 0;
824         }
825         else {
826                 MaskSplinePointUW *new_uw;
827                 int j = 0;
828
829                 new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
830
831                 for (i = 0; i < point->tot_uw; i++) {
832                         if ((point->uw[i].flag & SELECT) == 0) {
833                                 new_uw[j++] = point->uw[i];
834                         }
835                 }
836
837                 MEM_freeN(point->uw);
838
839                 point->uw = new_uw;
840                 point->tot_uw = count;
841         }
842 }
843
844 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
845 {
846         Mask *mask = CTX_data_edit_mask(C);
847         MaskLayer *masklay;
848         int mask_layer_shape_ofs = 0;
849
850         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
851                 MaskSpline *spline;
852
853                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
854                         continue;
855                 }
856
857                 spline = masklay->splines.first;
858
859                 while (spline) {
860                         const int tot_point_orig = spline->tot_point;
861                         int i, count = 0;
862                         MaskSpline *next_spline = spline->next;
863
864                         /* count unselected points */
865                         for (i = 0; i < spline->tot_point; i++) {
866                                 MaskSplinePoint *point = &spline->points[i];
867
868                                 if (!MASKPOINT_ISSEL_ANY(point))
869                                         count++;
870                         }
871
872                         if (count == 0) {
873
874                                 /* delete the whole spline */
875                                 BLI_remlink(&masklay->splines, spline);
876                                 BKE_mask_spline_free(spline);
877
878                                 if (spline == masklay->act_spline) {
879                                         masklay->act_spline = NULL;
880                                         masklay->act_point = NULL;
881                                 }
882
883                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
884                         }
885                         else {
886                                 MaskSplinePoint *new_points;
887                                 int j;
888
889                                 new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
890
891                                 for (i = 0, j = 0; i < tot_point_orig; i++) {
892                                         MaskSplinePoint *point = &spline->points[i];
893
894                                         if (!MASKPOINT_ISSEL_ANY(point)) {
895                                                 if (point == masklay->act_point)
896                                                         masklay->act_point = &new_points[j];
897
898                                                 delete_feather_points(point);
899
900                                                 new_points[j] = *point;
901                                                 j++;
902                                         }
903                                         else {
904                                                 if (point == masklay->act_point)
905                                                         masklay->act_point = NULL;
906
907                                                 BKE_mask_point_free(point);
908                                                 spline->tot_point--;
909
910                                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
911                                         }
912                                 }
913
914                                 mask_layer_shape_ofs += spline->tot_point;
915
916                                 MEM_freeN(spline->points);
917                                 spline->points = new_points;
918
919                                 ED_mask_select_flush_all(mask);
920                         }
921
922                         spline = next_spline;
923                 }
924         }
925
926         /* TODO: only update edited splines */
927         BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
928
929         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
930
931         return OPERATOR_FINISHED;
932 }
933
934 void MASK_OT_delete(wmOperatorType *ot)
935 {
936         /* identifiers */
937         ot->name = "Delete";
938         ot->description = "Delete selected control points or splines";
939         ot->idname = "MASK_OT_delete";
940
941         /* api callbacks */
942         ot->invoke = WM_operator_confirm;
943         ot->exec = delete_exec;
944         ot->poll = ED_maskedit_mask_poll;
945
946         /* flags */
947         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
948 }
949
950 /* *** switch direction *** */
951 static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
952 {
953         Mask *mask = CTX_data_edit_mask(C);
954         MaskLayer *masklay;
955
956         int change = FALSE;
957
958         /* do actual selection */
959         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
960                 MaskSpline *spline;
961
962                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
963                         continue;
964                 }
965
966                 for (spline = masklay->splines.first; spline; spline = spline->next) {
967                         if (ED_mask_spline_select_check(spline)) {
968                                 BKE_mask_spline_direction_switch(masklay, spline);
969                                 change = TRUE;
970                         }
971                 }
972         }
973
974         if (change) {
975                 /* TODO: only update this spline */
976                 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
977
978                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
979
980                 return OPERATOR_FINISHED;
981         }
982
983         return OPERATOR_CANCELLED;
984 }
985
986 void MASK_OT_switch_direction(wmOperatorType *ot)
987 {
988         /* identifiers */
989         ot->name = "Switch Direction";
990         ot->description = "Switch direction of selected splines";
991         ot->idname = "MASK_OT_switch_direction";
992
993         /* api callbacks */
994         ot->exec = mask_switch_direction_exec;
995         ot->poll = ED_maskedit_mask_poll;
996
997         /* flags */
998         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
999 }
1000
1001
1002 /******************** set handle type *********************/
1003
1004 static int set_handle_type_exec(bContext *C, wmOperator *op)
1005 {
1006         Mask *mask = CTX_data_edit_mask(C);
1007         MaskLayer *masklay;
1008         int handle_type = RNA_enum_get(op->ptr, "type");
1009
1010         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1011                 MaskSpline *spline;
1012                 int i;
1013
1014                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1015                         continue;
1016                 }
1017
1018                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1019                         for (i = 0; i < spline->tot_point; i++) {
1020                                 MaskSplinePoint *point = &spline->points[i];
1021
1022                                 if (MASKPOINT_ISSEL_ANY(point)) {
1023                                         BezTriple *bezt = &point->bezt;
1024
1025                                         bezt->h1 = bezt->h2 = handle_type;
1026                                 }
1027                         }
1028                 }
1029         }
1030
1031         WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1032         DAG_id_tag_update(&mask->id, 0);
1033
1034         return OPERATOR_FINISHED;
1035 }
1036
1037 void MASK_OT_handle_type_set(wmOperatorType *ot)
1038 {
1039         static EnumPropertyItem editcurve_handle_type_items[] = {
1040                 {HD_AUTO, "AUTO", 0, "Auto", ""},
1041                 {HD_VECT, "VECTOR", 0, "Vector", ""},
1042                 {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
1043                 {0, NULL, 0, NULL, NULL}
1044         };
1045
1046         /* identifiers */
1047         ot->name = "Set Handle Type";
1048         ot->description = "Set type of handles for selected control points";
1049         ot->idname = "MASK_OT_handle_type_set";
1050
1051         /* api callbacks */
1052         ot->invoke = WM_menu_invoke;
1053         ot->exec = set_handle_type_exec;
1054         ot->poll = ED_maskedit_mask_poll;
1055
1056         /* flags */
1057         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1058
1059         /* properties */
1060         ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1061 }
1062
1063
1064 /* ********* clear/set restrict view *********/
1065 static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
1066 {
1067         Mask *mask = CTX_data_edit_mask(C);
1068         MaskLayer *masklay;
1069         int changed = FALSE;
1070
1071         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1072
1073                 if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1074                         ED_mask_layer_select_set(masklay, TRUE);
1075                         masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1076                         changed = 1;
1077                 }
1078         }
1079
1080         if (changed) {
1081                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1082                 DAG_id_tag_update(&mask->id, 0);
1083
1084                 return OPERATOR_FINISHED;
1085         }
1086         else {
1087                 return OPERATOR_CANCELLED;
1088         }
1089 }
1090
1091 void MASK_OT_hide_view_clear(wmOperatorType *ot)
1092 {
1093
1094         /* identifiers */
1095         ot->name = "Clear Restrict View";
1096         ot->description = "Reveal the layer by setting the hide flag";
1097         ot->idname = "MASK_OT_hide_view_clear";
1098
1099         /* api callbacks */
1100         ot->exec = mask_hide_view_clear_exec;
1101         ot->poll = ED_maskedit_mask_poll;
1102
1103         /* flags */
1104         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1105 }
1106
1107 static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
1108 {
1109         Mask *mask = CTX_data_edit_mask(C);
1110         MaskLayer *masklay;
1111         const int unselected = RNA_boolean_get(op->ptr, "unselected");
1112         int changed = FALSE;
1113
1114         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1115
1116                 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
1117                         continue;
1118                 }
1119
1120                 if (!unselected) {
1121                         if (ED_mask_layer_select_check(masklay)) {
1122                                 ED_mask_layer_select_set(masklay, FALSE);
1123
1124                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1125                                 changed = 1;
1126                                 if (masklay == BKE_mask_layer_active(mask)) {
1127                                         BKE_mask_layer_active_set(mask, NULL);
1128                                 }
1129                         }
1130                 }
1131                 else {
1132                         if (!ED_mask_layer_select_check(masklay)) {
1133                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1134                                 changed = 1;
1135                                 if (masklay == BKE_mask_layer_active(mask)) {
1136                                         BKE_mask_layer_active_set(mask, NULL);
1137                                 }
1138                         }
1139                 }
1140         }
1141
1142         if (changed) {
1143                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1144                 DAG_id_tag_update(&mask->id, 0);
1145
1146                 return OPERATOR_FINISHED;
1147         }
1148         else {
1149                 return OPERATOR_CANCELLED;
1150         }
1151 }
1152
1153 void MASK_OT_hide_view_set(wmOperatorType *ot)
1154 {
1155         /* identifiers */
1156         ot->name = "Set Restrict View";
1157         ot->description = "Hide the layer by setting the hide flag";
1158         ot->idname = "MASK_OT_hide_view_set";
1159
1160         /* api callbacks */
1161         ot->exec = mask_hide_view_set_exec;
1162         ot->poll = ED_maskedit_mask_poll;
1163
1164         /* flags */
1165         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1166
1167         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
1168
1169 }