Code cleanup: remove unused code
[blender.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_clip.h"
49 #include "ED_image.h"
50 #include "ED_keyframing.h"
51 #include "ED_mask.h"
52 #include "ED_screen.h"
53
54 #include "RNA_access.h"
55 #include "RNA_define.h"
56
57 #include "mask_intern.h"  /* own include */
58
59 /******************** utility functions *********************/
60
61 MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C, Mask *mask, float normal_co[2], int threshold,
62                                             MaskLayer **masklay_r, MaskSpline **spline_r, int *is_handle_r,
63                                             float *score)
64 {
65         ScrArea *sa = CTX_wm_area(C);
66         ARegion *ar = CTX_wm_region(C);
67
68         MaskLayer *masklay;
69         MaskLayer *point_masklay = NULL;
70         MaskSpline *point_spline = NULL;
71         MaskSplinePoint *point = NULL;
72         float co[2];
73         float len = FLT_MAX, scalex, scaley;
74         int is_handle = FALSE, width, height;
75
76         ED_mask_get_size(sa, &width, &height);
77         ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
78
79         co[0] = normal_co[0] * scalex;
80         co[1] = normal_co[1] * scaley;
81
82         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
83                 MaskSpline *spline;
84
85                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
86                         continue;
87                 }
88
89                 for (spline = masklay->splines.first; spline; spline = spline->next) {
90                         MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
91
92                         int i;
93
94                         for (i = 0; i < spline->tot_point; i++) {
95                                 MaskSplinePoint *cur_point = &spline->points[i];
96                                 MaskSplinePoint *cur_point_deform = &points_array[i];
97                                 float cur_len, vec[2], handle[2];
98
99                                 vec[0] = cur_point_deform->bezt.vec[1][0] * scalex;
100                                 vec[1] = cur_point_deform->bezt.vec[1][1] * scaley;
101
102                                 if (BKE_mask_point_has_handle(cur_point)) {
103                                         BKE_mask_point_handle(cur_point_deform, handle);
104                                         handle[0] *= scalex;
105                                         handle[1] *= scaley;
106
107                                         cur_len = len_v2v2(co, handle);
108
109                                         if (cur_len < len) {
110                                                 point_masklay = masklay;
111                                                 point_spline = spline;
112                                                 point = cur_point;
113                                                 len = cur_len;
114                                                 is_handle = TRUE;
115                                         }
116                                 }
117
118                                 cur_len = len_v2v2(co, vec);
119
120                                 if (cur_len < len) {
121                                         point_spline = spline;
122                                         point_masklay = masklay;
123                                         point = cur_point;
124                                         len = cur_len;
125                                         is_handle = FALSE;
126                                 }
127                         }
128                 }
129         }
130
131         if (len < threshold) {
132                 if (masklay_r)
133                         *masklay_r = point_masklay;
134
135                 if (spline_r)
136                         *spline_r = point_spline;
137
138                 if (is_handle_r)
139                         *is_handle_r = is_handle;
140
141                 if (score)
142                         *score = len;
143
144                 return point;
145         }
146
147         if (masklay_r)
148                 *masklay_r = NULL;
149
150         if (spline_r)
151                 *spline_r = NULL;
152
153         if (is_handle_r)
154                 *is_handle_r = FALSE;
155
156         return NULL;
157 }
158
159 int ED_mask_feather_find_nearest(const bContext *C, Mask *mask, float normal_co[2], int threshold,
160                                  MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
161                                  MaskSplinePointUW **uw_r, float *score)
162 {
163         ScrArea *sa = CTX_wm_area(C);
164         ARegion *ar = CTX_wm_region(C);
165
166         MaskLayer *masklay, *point_masklay = NULL;
167         MaskSpline *point_spline = NULL;
168         MaskSplinePoint *point = NULL;
169         MaskSplinePointUW *uw = NULL;
170         float len = FLT_MAX, co[2];
171         float scalex, scaley;
172         int width, height;
173
174         ED_mask_get_size(sa, &width, &height);
175         ED_mask_pixelspace_factor(sa, ar, &scalex, &scaley);
176
177         co[0] = normal_co[0] * scalex;
178         co[1] = normal_co[1] * scaley;
179
180         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
181                 MaskSpline *spline;
182
183                 for (spline = masklay->splines.first; spline; spline = spline->next) {
184                         //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
185
186                         int i, tot_feather_point;
187                         float (*feather_points)[2], (*fp)[2];
188
189                         if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
190                                 continue;
191                         }
192
193                         feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
194
195                         for (i = 0; i < spline->tot_point; i++) {
196                                 int j;
197                                 MaskSplinePoint *cur_point = &spline->points[i];
198
199                                 for (j = 0; j < cur_point->tot_uw + 1; j++) {
200                                         float cur_len, vec[2];
201
202                                         vec[0] = (*fp)[0] * scalex;
203                                         vec[1] = (*fp)[1] * scaley;
204
205                                         cur_len = len_v2v2(vec, co);
206
207                                         if (point == NULL || cur_len < len) {
208                                                 if (j == 0)
209                                                         uw = NULL;
210                                                 else
211                                                         uw = &cur_point->uw[j - 1];
212
213                                                 point_masklay = masklay;
214                                                 point_spline = spline;
215                                                 point = cur_point;
216                                                 len = cur_len;
217                                         }
218
219                                         fp++;
220                                 }
221                         }
222
223                         MEM_freeN(feather_points);
224                 }
225         }
226
227         if (len < threshold) {
228                 if (masklay_r)
229                         *masklay_r = point_masklay;
230
231                 if (spline_r)
232                         *spline_r = point_spline;
233
234                 if (point_r)
235                         *point_r = point;
236
237                 if (uw_r)
238                         *uw_r = uw;
239
240                 if (score)
241                         *score = len;
242
243                 return TRUE;
244         }
245
246         if (masklay_r)
247                 *masklay_r = NULL;
248
249         if (spline_r)
250                 *spline_r = NULL;
251
252         if (point_r)
253                 *point_r = NULL;
254
255         return FALSE;
256 }
257
258
259 /******************** create new mask *********************/
260
261 static int mask_new_exec(bContext *C, wmOperator *op)
262 {
263         ScrArea *sa = CTX_wm_area(C);
264         Mask *mask;
265         char name[MAX_ID_NAME - 2];
266
267         RNA_string_get(op->ptr, "name", name);
268
269         mask = BKE_mask_new(name);
270
271         if (sa && sa->spacedata.first) {
272                 switch (sa->spacetype) {
273                         case SPACE_CLIP:
274                         {
275                                 SpaceClip *sc = sa->spacedata.first;
276                                 ED_space_clip_set_mask(C, sc, mask);
277                                 break;
278                         }
279                         case SPACE_SEQ:
280                         {
281                                 /* do nothing */
282                                 break;
283                         }
284                         case SPACE_IMAGE:
285                         {
286                                 SpaceImage *sima = sa->spacedata.first;
287                                 ED_space_image_set_mask(C, sima, mask);
288                                 break;
289                         }
290                 }
291         }
292
293         return OPERATOR_FINISHED;
294 }
295
296 void MASK_OT_new(wmOperatorType *ot)
297 {
298         /* identifiers */
299         ot->name = "New Mask";
300         ot->description = "Create new mask";
301         ot->idname = "MASK_OT_new";
302
303         /* flags */
304         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
305
306         /* api callbacks */
307         ot->exec = mask_new_exec;
308         ot->poll = ED_operator_mask;
309
310         /* properties */
311         RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask");
312 }
313
314 /******************** create new masklay *********************/
315
316 static int masklay_new_exec(bContext *C, wmOperator *op)
317 {
318         Mask *mask = CTX_data_edit_mask(C);
319         char name[MAX_ID_NAME - 2];
320
321         RNA_string_get(op->ptr, "name", name);
322
323         BKE_mask_layer_new(mask, name);
324         mask->masklay_act = mask->masklay_tot - 1;
325
326         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
327
328         return OPERATOR_FINISHED;
329 }
330
331 void MASK_OT_layer_new(wmOperatorType *ot)
332 {
333         /* identifiers */
334         ot->name = "Add Mask Layer";
335         ot->description = "Add new mask layer for masking";
336         ot->idname = "MASK_OT_layer_new";
337
338         /* api callbacks */
339         ot->exec = masklay_new_exec;
340         ot->poll = ED_maskedit_poll;
341
342         /* flags */
343         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
344
345         /* properties */
346         RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask layer");
347 }
348
349 /******************** remove mask layer *********************/
350
351 static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
352 {
353         Mask *mask = CTX_data_edit_mask(C);
354         MaskLayer *masklay = BKE_mask_layer_active(mask);
355
356         if (masklay) {
357                 BKE_mask_layer_remove(mask, masklay);
358
359                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
360         }
361
362         return OPERATOR_FINISHED;
363 }
364
365 void MASK_OT_layer_remove(wmOperatorType *ot)
366 {
367         /* identifiers */
368         ot->name = "Remove Mask Layer";
369         ot->description = "Remove mask layer";
370         ot->idname = "MASK_OT_layer_remove";
371
372         /* api callbacks */
373         ot->exec = masklay_remove_exec;
374         ot->poll = ED_maskedit_poll;
375
376         /* flags */
377         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
378 }
379
380 /******************** slide *********************/
381
382 enum {
383         SLIDE_ACTION_NONE    = 0,
384         SLIDE_ACTION_POINT   = 1,
385         SLIDE_ACTION_HANDLE  = 2,
386         SLIDE_ACTION_FEATHER = 3
387 };
388
389 typedef struct SlidePointData {
390         int action;
391
392         float co[2];
393         float vec[3][3];
394
395         Mask *mask;
396         MaskLayer *masklay;
397         MaskSpline *spline, *orig_spline;
398         MaskSplinePoint *point;
399         MaskSplinePointUW *uw;
400         float handle[2], no[2], feather[2];
401         int width, height;
402         float weight, weight_scalar;
403
404         short curvature_only, accurate;
405         short initial_feather, overall_feather;
406 } SlidePointData;
407
408 static int slide_point_check_initial_feather(MaskSpline *spline)
409 {
410         int i;
411
412         for (i = 0; i < spline->tot_point; i++) {
413                 MaskSplinePoint *point = &spline->points[i];
414
415                 if (point->bezt.weight != 0.0f)
416                         return FALSE;
417
418                 /* comment for now. if all bezt weights are zero - this is as good-as initial */
419 #if 0
420                 int j;
421                 for (j = 0; j < point->tot_uw; j++) {
422                         if (point->uw[j].w != 0.0f)
423                                 return FALSE;
424                 }
425 #endif
426         }
427
428         return TRUE;
429 }
430
431 static void *slide_point_customdata(bContext *C, wmOperator *op, wmEvent *event)
432 {
433         ScrArea *sa = CTX_wm_area(C);
434         ARegion *ar = CTX_wm_region(C);
435
436         Mask *mask = CTX_data_edit_mask(C);
437         SlidePointData *customdata = NULL;
438         MaskLayer *masklay, *cv_masklay, *feather_masklay;
439         MaskSpline *spline, *cv_spline, *feather_spline;
440         MaskSplinePoint *point, *cv_point, *feather_point;
441         MaskSplinePointUW *uw = NULL;
442         int is_handle = FALSE, width, height, action = SLIDE_ACTION_NONE;
443         int slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
444         float co[2], cv_score, feather_score;
445         const float threshold = 19;
446
447         ED_mask_mouse_pos(sa, ar, event->mval, co);
448         ED_mask_get_size(sa, &width, &height);
449
450         cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &is_handle, &cv_score);
451
452         if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
453                 if (slide_feather || !cv_point || feather_score < cv_score) {
454                         action = SLIDE_ACTION_FEATHER;
455
456                         masklay = feather_masklay;
457                         spline = feather_spline;
458                         point = feather_point;
459                 }
460         }
461
462         if (cv_point && action == SLIDE_ACTION_NONE) {
463                 if (is_handle)
464                         action = SLIDE_ACTION_HANDLE;
465                 else
466                         action = SLIDE_ACTION_POINT;
467
468                 masklay = cv_masklay;
469                 spline = cv_spline;
470                 point = cv_point;
471         }
472
473         if (action != SLIDE_ACTION_NONE) {
474                 customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
475
476                 customdata->mask = mask;
477                 customdata->masklay = masklay;
478                 customdata->spline = spline;
479                 customdata->point = point;
480                 customdata->width = width;
481                 customdata->height = height;
482                 customdata->action = action;
483                 customdata->uw = uw;
484
485                 if (uw) {
486                         float co[2];
487                         float weight_scalar = BKE_mask_point_weight_scalar(spline, point, uw->u);
488
489                         customdata->weight = uw->w;
490                         customdata->weight_scalar = weight_scalar;
491                         BKE_mask_point_segment_co(spline, point, uw->u, co);
492                         BKE_mask_point_normal(spline, point, uw->u, customdata->no);
493
494                         madd_v2_v2v2fl(customdata->feather, co, customdata->no, uw->w * weight_scalar);
495                 }
496                 else {
497                         BezTriple *bezt = &point->bezt;
498
499                         customdata->weight = bezt->weight;
500                         customdata->weight_scalar = 1.0f;
501                         BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
502
503                         madd_v2_v2v2fl(customdata->feather, bezt->vec[1], customdata->no, bezt->weight);
504                 }
505
506                 if (customdata->action == SLIDE_ACTION_FEATHER)
507                         customdata->initial_feather = slide_point_check_initial_feather(spline);
508
509                 copy_m3_m3(customdata->vec, point->bezt.vec);
510                 if (BKE_mask_point_has_handle(point))
511                         BKE_mask_point_handle(point, customdata->handle);
512                 ED_mask_mouse_pos(sa, ar, event->mval, customdata->co);
513         }
514
515         return customdata;
516 }
517
518 static int slide_point_invoke(bContext *C, wmOperator *op, wmEvent *event)
519 {
520         SlidePointData *slidedata = slide_point_customdata(C, op, event);
521
522         if (slidedata) {
523                 Mask *mask = CTX_data_edit_mask(C);
524
525                 op->customdata = slidedata;
526
527                 WM_event_add_modal_handler(C, op);
528
529                 if (slidedata->uw) {
530                         if ((slidedata->uw->flag & SELECT) == 0) {
531                                 ED_mask_select_toggle_all(mask, SEL_DESELECT);
532
533                                 slidedata->uw->flag |= SELECT;
534
535                                 ED_mask_select_flush_all(mask);
536                         }
537                 }
538                 else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) {
539                         ED_mask_select_toggle_all(mask, SEL_DESELECT);
540
541                         BKE_mask_point_select_set(slidedata->point, TRUE);
542
543                         ED_mask_select_flush_all(mask);
544                 }
545
546                 slidedata->masklay->act_spline = slidedata->spline;
547                 slidedata->masklay->act_point = slidedata->point;
548
549                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
550
551                 return OPERATOR_RUNNING_MODAL;
552         }
553
554         return OPERATOR_PASS_THROUGH;
555 }
556
557 static void slide_point_delta_all_feather(SlidePointData *data, float delta)
558 {
559         int i;
560
561         for (i = 0; i < data->spline->tot_point; i++) {
562                 MaskSplinePoint *point = &data->spline->points[i];
563                 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
564
565                 point->bezt.weight = orig_point->bezt.weight + delta;
566                 if (point->bezt.weight < 0.0f)
567                         point->bezt.weight = 0.0f;
568
569                 /* not needed anymore */
570 #if 0
571                 int j;
572                 for (j = 0; j < point->tot_uw; j++) {
573                         point->uw[j].w = orig_point->uw[j].w + delta;
574                         if (point->uw[j].w < 0.0f)
575                                 point->uw[j].w = 0.0f;
576                 }
577 #endif
578         }
579 }
580
581 static void slide_point_restore_spline(SlidePointData *data)
582 {
583         int i;
584
585         for (i = 0; i < data->spline->tot_point; i++) {
586                 MaskSplinePoint *point = &data->spline->points[i];
587                 MaskSplinePoint *orig_point = &data->orig_spline->points[i];
588                 int j;
589
590                 point->bezt = orig_point->bezt;
591
592                 for (j = 0; j < point->tot_uw; j++)
593                         point->uw[j] = orig_point->uw[j];
594         }
595 }
596
597 static void cancel_slide_point(SlidePointData *data)
598 {
599         /* cancel sliding */
600
601         if (data->orig_spline) {
602                 slide_point_restore_spline(data);
603         }
604         else {
605                 if (data->action == SLIDE_ACTION_FEATHER) {
606                         if (data->uw)
607                                 data->uw->w = data->weight;
608                         else
609                                 data->point->bezt.weight = data->weight;
610                 }
611                 else {
612                         copy_m3_m3(data->point->bezt.vec, data->vec);
613                 }
614         }
615 }
616
617 static void free_slide_point_data(SlidePointData *data)
618 {
619         if (data->orig_spline)
620                 BKE_mask_spline_free(data->orig_spline);
621
622         MEM_freeN(data);
623 }
624
625 static int slide_point_modal(bContext *C, wmOperator *op, wmEvent *event)
626 {
627         SlidePointData *data = (SlidePointData *)op->customdata;
628         BezTriple *bezt = &data->point->bezt;
629         float co[2], dco[2];
630
631         switch (event->type) {
632                 case LEFTCTRLKEY:
633                 case RIGHTCTRLKEY:
634                 case LEFTSHIFTKEY:
635                 case RIGHTSHIFTKEY:
636                         if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY)) {
637                                 if (data->action == SLIDE_ACTION_FEATHER)
638                                         data->overall_feather = event->val == KM_PRESS;
639                                 else
640                                         data->curvature_only = event->val == KM_PRESS;
641                         }
642
643                         if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
644                                 data->accurate = event->val == KM_PRESS;
645
646                 /* no break! update CV position */
647
648                 case MOUSEMOVE:
649                 {
650                         ScrArea *sa = CTX_wm_area(C);
651                         ARegion *ar = CTX_wm_region(C);
652
653                         ED_mask_mouse_pos(sa, ar, event->mval, co);
654                         sub_v2_v2v2(dco, co, data->co);
655
656                         if (data->action == SLIDE_ACTION_HANDLE) {
657                                 float delta[2], offco[2];
658
659                                 sub_v2_v2v2(delta, data->handle, data->co);
660
661                                 sub_v2_v2v2(offco, co, data->co);
662                                 if (data->accurate)
663                                         mul_v2_fl(offco, 0.2f);
664                                 add_v2_v2(offco, data->co);
665                                 add_v2_v2(offco, delta);
666
667                                 BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->handle, data->vec);
668                         }
669                         else if (data->action == SLIDE_ACTION_POINT) {
670                                 float delta[2];
671
672                                 copy_v2_v2(delta, dco);
673                                 if (data->accurate)
674                                         mul_v2_fl(delta, 0.2f);
675
676                                 add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
677                                 add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
678                                 add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
679                         }
680                         else if (data->action == SLIDE_ACTION_FEATHER) {
681                                 float vec[2], no[2], p[2], c[2], w, offco[2];
682                                 float *weight = NULL;
683                                 float weight_scalar = 1.0f;
684                                 int overall_feather = data->overall_feather || data->initial_feather;
685
686                                 add_v2_v2v2(offco, data->feather, dco);
687
688                                 if (data->uw) {
689                                         /* project on both sides and find the closest one,
690                                          * prevents flickering when projecting onto both sides can happen */
691                                         const float u_pos = BKE_mask_spline_project_co(data->spline, data->point,
692                                                                                        data->uw->u, offco, MASK_PROJ_NEG);
693                                         const float u_neg = BKE_mask_spline_project_co(data->spline, data->point,
694                                                                                        data->uw->u, offco, MASK_PROJ_POS);
695                                         float dist_pos = FLT_MAX;
696                                         float dist_neg = FLT_MAX;
697                                         float co_pos[2];
698                                         float co_neg[2];
699                                         float u;
700
701                                         if (u_pos > 0.0f && u_pos < 1.0f) {
702                                                 BKE_mask_point_segment_co(data->spline, data->point, u_pos, co_pos);
703                                                 dist_pos = len_squared_v2v2(offco, co_pos);
704                                         }
705
706                                         if (u_neg > 0.0f && u_neg < 1.0f) {
707                                                 BKE_mask_point_segment_co(data->spline, data->point, u_neg, co_neg);
708                                                 dist_neg = len_squared_v2v2(offco, co_neg);
709                                         }
710
711                                         u = dist_pos < dist_neg ? u_pos : u_neg;
712
713                                         if (u > 0.0f && u < 1.0f) {
714                                                 data->uw->u = u;
715
716                                                 data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
717                                                 weight = &data->uw->w;
718                                                 weight_scalar = BKE_mask_point_weight_scalar(data->spline, data->point, u);
719                                                 if (weight_scalar != 0.0f) {
720                                                         weight_scalar = 1.0f / weight_scalar;
721                                                 }
722
723                                                 BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
724                                                 BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
725                                         }
726                                 }
727                                 else {
728                                         weight = &bezt->weight;
729                                         /* weight_scalar = 1.0f; keep as is */
730                                         copy_v2_v2(no, data->no);
731                                         copy_v2_v2(p, bezt->vec[1]);
732                                 }
733
734                                 if (weight) {
735                                         sub_v2_v2v2(c, offco, p);
736                                         project_v2_v2v2(vec, c, no);
737
738                                         w = len_v2(vec);
739
740                                         if (overall_feather) {
741                                                 float delta;
742
743                                                 if (dot_v2v2(no, vec) <= 0.0f)
744                                                         w = -w;
745
746                                                 delta = w - data->weight * data->weight_scalar;
747
748                                                 if (data->orig_spline == NULL) {
749                                                         /* restore weight for currently sliding point, so orig_spline would be created
750                                                          * with original weights used
751                                                          */
752                                                         *weight = data->weight;
753
754                                                         data->orig_spline = BKE_mask_spline_copy(data->spline);
755                                                 }
756
757                                                 slide_point_delta_all_feather(data, delta);
758                                         }
759                                         else {
760                                                 if (dot_v2v2(no, vec) <= 0.0f)
761                                                         w = 0.0f;
762
763                                                 if (data->orig_spline) {
764                                                         /* restore possible overall feather changes */
765                                                         slide_point_restore_spline(data);
766
767                                                         BKE_mask_spline_free(data->orig_spline);
768                                                         data->orig_spline = NULL;
769                                                 }
770
771                                                 if (weight_scalar != 0.0f) {
772                                                         *weight = w * weight_scalar;
773                                                 }
774                                         }
775                                 }
776                         }
777
778                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
779                         DAG_id_tag_update(&data->mask->id, 0);
780
781                         break;
782                 }
783
784                 case LEFTMOUSE:
785                         if (event->val == KM_RELEASE) {
786                                 Scene *scene = CTX_data_scene(C);
787
788                                 /* dont key sliding feather uw's */
789                                 if ((data->action == SLIDE_ACTION_FEATHER && data->uw) == FALSE) {
790                                         if (IS_AUTOKEY_ON(scene)) {
791                                                 ED_mask_layer_shape_auto_key(data->masklay, CFRA);
792                                         }
793                                 }
794
795                                 WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
796                                 DAG_id_tag_update(&data->mask->id, 0);
797
798                                 free_slide_point_data(op->customdata); /* keep this last! */
799                                 return OPERATOR_FINISHED;
800                         }
801
802                         break;
803
804                 case ESCKEY:
805                         cancel_slide_point(op->customdata);
806
807                         WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
808                         DAG_id_tag_update(&data->mask->id, 0);
809
810                         free_slide_point_data(op->customdata); /* keep this last! */
811                         return OPERATOR_CANCELLED;
812         }
813
814         return OPERATOR_RUNNING_MODAL;
815 }
816
817 void MASK_OT_slide_point(wmOperatorType *ot)
818 {
819         /* identifiers */
820         ot->name = "Slide Point";
821         ot->description = "Slide control points";
822         ot->idname = "MASK_OT_slide_point";
823
824         /* api callbacks */
825         ot->invoke = slide_point_invoke;
826         ot->modal = slide_point_modal;
827         ot->poll = ED_maskedit_mask_poll;
828
829         /* flags */
830         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
831
832         RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide feather instead of vertex");
833 }
834
835 /******************** toggle cyclic *********************/
836
837 static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
838 {
839         Mask *mask = CTX_data_edit_mask(C);
840         MaskLayer *masklay;
841
842         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
843                 MaskSpline *spline;
844
845                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
846                         continue;
847                 }
848
849                 for (spline = masklay->splines.first; spline; spline = spline->next) {
850                         if (ED_mask_spline_select_check(spline)) {
851                                 spline->flag ^= MASK_SPLINE_CYCLIC;
852                         }
853                 }
854         }
855
856         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
857
858         return OPERATOR_FINISHED;
859 }
860
861 void MASK_OT_cyclic_toggle(wmOperatorType *ot)
862 {
863         /* identifiers */
864         ot->name = "Toggle Cyclic";
865         ot->description = "Toggle cyclic for selected splines";
866         ot->idname = "MASK_OT_cyclic_toggle";
867
868         /* api callbacks */
869         ot->exec = cyclic_toggle_exec;
870         ot->poll = ED_maskedit_mask_poll;
871
872         /* flags */
873         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
874 }
875
876 /******************** delete *********************/
877
878 static void delete_feather_points(MaskSplinePoint *point)
879 {
880         int i, count = 0;
881
882         if (!point->tot_uw)
883                 return;
884
885         for (i = 0; i < point->tot_uw; i++) {
886                 if ((point->uw[i].flag & SELECT) == 0)
887                         count++;
888         }
889
890         if (count == 0) {
891                 MEM_freeN(point->uw);
892                 point->uw = NULL;
893                 point->tot_uw = 0;
894         }
895         else {
896                 MaskSplinePointUW *new_uw;
897                 int j = 0;
898
899                 new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
900
901                 for (i = 0; i < point->tot_uw; i++) {
902                         if ((point->uw[i].flag & SELECT) == 0) {
903                                 new_uw[j++] = point->uw[i];
904                         }
905                 }
906
907                 MEM_freeN(point->uw);
908
909                 point->uw = new_uw;
910                 point->tot_uw = count;
911         }
912 }
913
914 static int delete_exec(bContext *C, wmOperator *UNUSED(op))
915 {
916         Mask *mask = CTX_data_edit_mask(C);
917         MaskLayer *masklay;
918
919         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
920                 MaskSpline *spline;
921                 int mask_layer_shape_ofs = 0;
922
923                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
924                         continue;
925                 }
926
927                 spline = masklay->splines.first;
928
929                 while (spline) {
930                         const int tot_point_orig = spline->tot_point;
931                         int i, count = 0;
932                         MaskSpline *next_spline = spline->next;
933
934                         /* count unselected points */
935                         for (i = 0; i < spline->tot_point; i++) {
936                                 MaskSplinePoint *point = &spline->points[i];
937
938                                 if (!MASKPOINT_ISSEL_ANY(point))
939                                         count++;
940                         }
941
942                         if (count == 0) {
943
944                                 /* delete the whole spline */
945                                 BLI_remlink(&masklay->splines, spline);
946                                 BKE_mask_spline_free(spline);
947
948                                 if (spline == masklay->act_spline) {
949                                         masklay->act_spline = NULL;
950                                         masklay->act_point = NULL;
951                                 }
952
953                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
954                         }
955                         else {
956                                 MaskSplinePoint *new_points;
957                                 int j;
958
959                                 new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
960
961                                 for (i = 0, j = 0; i < tot_point_orig; i++) {
962                                         MaskSplinePoint *point = &spline->points[i];
963
964                                         if (!MASKPOINT_ISSEL_ANY(point)) {
965                                                 if (point == masklay->act_point)
966                                                         masklay->act_point = &new_points[j];
967
968                                                 delete_feather_points(point);
969
970                                                 new_points[j] = *point;
971                                                 j++;
972                                         }
973                                         else {
974                                                 if (point == masklay->act_point)
975                                                         masklay->act_point = NULL;
976
977                                                 BKE_mask_point_free(point);
978                                                 spline->tot_point--;
979
980                                                 BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
981                                         }
982                                 }
983
984                                 mask_layer_shape_ofs += spline->tot_point;
985
986                                 MEM_freeN(spline->points);
987                                 spline->points = new_points;
988
989                                 ED_mask_select_flush_all(mask);
990                         }
991
992                         spline = next_spline;
993                 }
994
995                 /* not essential but confuses users when there are keys with no data!
996                  * assume if they delete all data from the layer they also dont care about keys */
997                 if (masklay->splines.first == NULL) {
998                         BKE_mask_layer_free_shapes(masklay);
999                 }
1000         }
1001
1002         /* TODO: only update edited splines */
1003         BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1004
1005         WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
1006
1007         return OPERATOR_FINISHED;
1008 }
1009
1010 void MASK_OT_delete(wmOperatorType *ot)
1011 {
1012         /* identifiers */
1013         ot->name = "Delete";
1014         ot->description = "Delete selected control points or splines";
1015         ot->idname = "MASK_OT_delete";
1016
1017         /* api callbacks */
1018         ot->invoke = WM_operator_confirm;
1019         ot->exec = delete_exec;
1020         ot->poll = ED_maskedit_mask_poll;
1021
1022         /* flags */
1023         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1024 }
1025
1026 /* *** switch direction *** */
1027 static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
1028 {
1029         Scene *scene = CTX_data_scene(C);
1030         Mask *mask = CTX_data_edit_mask(C);
1031         MaskLayer *masklay;
1032
1033         int change = FALSE;
1034
1035         /* do actual selection */
1036         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1037                 MaskSpline *spline;
1038                 int change_layer = FALSE;
1039
1040                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1041                         continue;
1042                 }
1043
1044                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1045                         if (ED_mask_spline_select_check(spline)) {
1046                                 BKE_mask_spline_direction_switch(masklay, spline);
1047                                 change = TRUE;
1048                                 change_layer = TRUE;
1049                         }
1050                 }
1051
1052                 if (change_layer) {
1053                         if (IS_AUTOKEY_ON(scene)) {
1054                                 ED_mask_layer_shape_auto_key(masklay, CFRA);
1055                         }
1056                 }
1057         }
1058
1059         if (change) {
1060                 /* TODO: only update this spline */
1061                 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1062
1063                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1064
1065                 return OPERATOR_FINISHED;
1066         }
1067
1068         return OPERATOR_CANCELLED;
1069 }
1070
1071 void MASK_OT_switch_direction(wmOperatorType *ot)
1072 {
1073         /* identifiers */
1074         ot->name = "Switch Direction";
1075         ot->description = "Switch direction of selected splines";
1076         ot->idname = "MASK_OT_switch_direction";
1077
1078         /* api callbacks */
1079         ot->exec = mask_switch_direction_exec;
1080         ot->poll = ED_maskedit_mask_poll;
1081
1082         /* flags */
1083         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1084 }
1085
1086
1087 /* *** recalc normals *** */
1088 static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op))
1089 {
1090         Scene *scene = CTX_data_scene(C);
1091         Mask *mask = CTX_data_edit_mask(C);
1092         MaskLayer *masklay;
1093         int i;
1094
1095         int change = FALSE;
1096
1097         /* do actual selection */
1098         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1099                 MaskSpline *spline;
1100                 int change_layer = FALSE;
1101
1102                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1103                         continue;
1104                 }
1105
1106                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1107                         for (i = 0; i < spline->tot_point; i++) {
1108                                 MaskSplinePoint *point = &spline->points[i];
1109
1110                                 if (MASKPOINT_ISSEL_ANY(point)) {
1111                                         BKE_mask_calc_handle_point_auto(spline, point, FALSE);
1112                                         change = TRUE;
1113                                         change_layer = TRUE;
1114                                 }
1115                         }
1116                 }
1117
1118                 if (change_layer) {
1119                         if (IS_AUTOKEY_ON(scene)) {
1120                                 ED_mask_layer_shape_auto_key(masklay, CFRA);
1121                         }
1122                 }
1123         }
1124
1125         if (change) {
1126                 /* TODO: only update this spline */
1127                 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1128
1129                 WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
1130
1131                 return OPERATOR_FINISHED;
1132         }
1133
1134         return OPERATOR_CANCELLED;
1135 }
1136
1137 /* named to match mesh recalc normals */
1138 void MASK_OT_normals_make_consistent(wmOperatorType *ot)
1139 {
1140         /* identifiers */
1141         ot->name = "Recalc Normals";
1142         ot->description = "Re-calculate the direction of selected handles";
1143         ot->idname = "MASK_OT_normals_make_consistent";
1144
1145         /* api callbacks */
1146         ot->exec = mask_normals_make_consistent_exec;
1147         ot->poll = ED_maskedit_mask_poll;
1148
1149         /* flags */
1150         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1151 }
1152
1153
1154 /******************** set handle type *********************/
1155
1156 static int set_handle_type_exec(bContext *C, wmOperator *op)
1157 {
1158         Mask *mask = CTX_data_edit_mask(C);
1159         MaskLayer *masklay;
1160         int handle_type = RNA_enum_get(op->ptr, "type");
1161
1162         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1163                 MaskSpline *spline;
1164                 int i;
1165
1166                 if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
1167                         continue;
1168                 }
1169
1170                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1171                         for (i = 0; i < spline->tot_point; i++) {
1172                                 MaskSplinePoint *point = &spline->points[i];
1173
1174                                 if (MASKPOINT_ISSEL_ANY(point)) {
1175                                         BezTriple *bezt = &point->bezt;
1176
1177                                         bezt->h1 = bezt->h2 = handle_type;
1178                                 }
1179                         }
1180                 }
1181         }
1182
1183         WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
1184         DAG_id_tag_update(&mask->id, 0);
1185
1186         return OPERATOR_FINISHED;
1187 }
1188
1189 void MASK_OT_handle_type_set(wmOperatorType *ot)
1190 {
1191         static EnumPropertyItem editcurve_handle_type_items[] = {
1192                 {HD_AUTO, "AUTO", 0, "Auto", ""},
1193                 {HD_VECT, "VECTOR", 0, "Vector", ""},
1194                 {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
1195                 {0, NULL, 0, NULL, NULL}
1196         };
1197
1198         /* identifiers */
1199         ot->name = "Set Handle Type";
1200         ot->description = "Set type of handles for selected control points";
1201         ot->idname = "MASK_OT_handle_type_set";
1202
1203         /* api callbacks */
1204         ot->invoke = WM_menu_invoke;
1205         ot->exec = set_handle_type_exec;
1206         ot->poll = ED_maskedit_mask_poll;
1207
1208         /* flags */
1209         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1210
1211         /* properties */
1212         ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
1213 }
1214
1215
1216 /* ********* clear/set restrict view *********/
1217 static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
1218 {
1219         Mask *mask = CTX_data_edit_mask(C);
1220         MaskLayer *masklay;
1221         int changed = FALSE;
1222
1223         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1224
1225                 if (masklay->restrictflag & OB_RESTRICT_VIEW) {
1226                         ED_mask_layer_select_set(masklay, TRUE);
1227                         masklay->restrictflag &= ~OB_RESTRICT_VIEW;
1228                         changed = 1;
1229                 }
1230         }
1231
1232         if (changed) {
1233                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1234                 DAG_id_tag_update(&mask->id, 0);
1235
1236                 return OPERATOR_FINISHED;
1237         }
1238         else {
1239                 return OPERATOR_CANCELLED;
1240         }
1241 }
1242
1243 void MASK_OT_hide_view_clear(wmOperatorType *ot)
1244 {
1245
1246         /* identifiers */
1247         ot->name = "Clear Restrict View";
1248         ot->description = "Reveal the layer by setting the hide flag";
1249         ot->idname = "MASK_OT_hide_view_clear";
1250
1251         /* api callbacks */
1252         ot->exec = mask_hide_view_clear_exec;
1253         ot->poll = ED_maskedit_mask_poll;
1254
1255         /* flags */
1256         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1257 }
1258
1259 static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
1260 {
1261         Mask *mask = CTX_data_edit_mask(C);
1262         MaskLayer *masklay;
1263         const int unselected = RNA_boolean_get(op->ptr, "unselected");
1264         int changed = FALSE;
1265
1266         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1267
1268                 if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
1269                         continue;
1270                 }
1271
1272                 if (!unselected) {
1273                         if (ED_mask_layer_select_check(masklay)) {
1274                                 ED_mask_layer_select_set(masklay, FALSE);
1275
1276                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1277                                 changed = 1;
1278                                 if (masklay == BKE_mask_layer_active(mask)) {
1279                                         BKE_mask_layer_active_set(mask, NULL);
1280                                 }
1281                         }
1282                 }
1283                 else {
1284                         if (!ED_mask_layer_select_check(masklay)) {
1285                                 masklay->restrictflag |= OB_RESTRICT_VIEW;
1286                                 changed = 1;
1287                                 if (masklay == BKE_mask_layer_active(mask)) {
1288                                         BKE_mask_layer_active_set(mask, NULL);
1289                                 }
1290                         }
1291                 }
1292         }
1293
1294         if (changed) {
1295                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1296                 DAG_id_tag_update(&mask->id, 0);
1297
1298                 return OPERATOR_FINISHED;
1299         }
1300         else {
1301                 return OPERATOR_CANCELLED;
1302         }
1303 }
1304
1305 void MASK_OT_hide_view_set(wmOperatorType *ot)
1306 {
1307         /* identifiers */
1308         ot->name = "Set Restrict View";
1309         ot->description = "Hide the layer by setting the hide flag";
1310         ot->idname = "MASK_OT_hide_view_set";
1311
1312         /* api callbacks */
1313         ot->exec = mask_hide_view_set_exec;
1314         ot->poll = ED_maskedit_mask_poll;
1315
1316         /* flags */
1317         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1318
1319         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
1320 }
1321
1322
1323 static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
1324 {
1325         Mask *mask = CTX_data_edit_mask(C);
1326         MaskLayer *masklay;
1327         int changed = FALSE;
1328         int i;
1329
1330         for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
1331                 MaskSpline *spline;
1332
1333                 if (masklay->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
1334                         continue;
1335                 }
1336
1337                 for (spline = masklay->splines.first; spline; spline = spline->next) {
1338                         for (i = 0; i < spline->tot_point; i++) {
1339                                 MaskSplinePoint *point = &spline->points[i];
1340
1341                                 if (MASKPOINT_ISSEL_ANY(point)) {
1342                                         BezTriple *bezt = &point->bezt;
1343                                         bezt->weight = 0.0f;
1344                                         changed = TRUE;
1345                                 }
1346                         }
1347                 }
1348         }
1349
1350         if (changed) {
1351                 /* TODO: only update edited splines */
1352                 BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
1353
1354                 WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
1355                 DAG_id_tag_update(&mask->id, 0);
1356
1357                 return OPERATOR_FINISHED;
1358         }
1359         else {
1360                 return OPERATOR_CANCELLED;
1361         }
1362 }
1363
1364 void MASK_OT_feather_weight_clear(wmOperatorType *ot)
1365 {
1366         /* identifiers */
1367         ot->name = "Clear Feather Weight";
1368         ot->description = "Reset the feather weight to zero";
1369         ot->idname = "MASK_OT_feather_weight_clear";
1370
1371         /* api callbacks */
1372         ot->exec = mask_feather_weight_clear_exec;
1373         ot->poll = ED_maskedit_mask_poll;
1374
1375         /* flags */
1376         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1377 }
1378
1379 /******************** move mask layer operator *********************/
1380
1381 static int mask_layer_move_poll(bContext *C)
1382 {
1383         if (ED_maskedit_mask_poll(C)) {
1384                 Mask *mask = CTX_data_edit_mask(C);
1385
1386                 return mask->masklay_tot > 0;
1387         }
1388
1389         return FALSE;
1390 }
1391
1392 static int mask_layer_move_exec(bContext *C, wmOperator *op)
1393 {
1394         Mask *mask = CTX_data_edit_mask(C);
1395         MaskLayer *mask_layer = BLI_findlink(&mask->masklayers, mask->masklay_act);
1396         MaskLayer *mask_layer_other;
1397         int direction = RNA_enum_get(op->ptr, "direction");
1398
1399         if (!mask_layer)
1400                 return OPERATOR_CANCELLED;
1401
1402         if (direction == -1) {
1403                 mask_layer_other = mask_layer->prev;
1404
1405                 if (!mask_layer_other)
1406                         return OPERATOR_CANCELLED;
1407
1408                 BLI_remlink(&mask->masklayers, mask_layer);
1409                 BLI_insertlinkbefore(&mask->masklayers, mask_layer_other, mask_layer);
1410                 mask->masklay_act--;
1411         }
1412         else if (direction == 1) {
1413                 mask_layer_other = mask_layer->next;
1414
1415                 if (!mask_layer_other)
1416                         return OPERATOR_CANCELLED;
1417
1418                 BLI_remlink(&mask->masklayers, mask_layer);
1419                 BLI_insertlinkafter(&mask->masklayers, mask_layer_other, mask_layer);
1420                 mask->masklay_act++;
1421         }
1422
1423         return OPERATOR_FINISHED;
1424 }
1425
1426 void MASK_OT_layer_move(wmOperatorType *ot)
1427 {
1428         static EnumPropertyItem direction_items[] = {
1429                 {-1, "UP", 0, "Up", ""},
1430                 {1, "DOWN", 0, "Down", ""},
1431                 {0, NULL, 0, NULL, NULL}
1432         };
1433
1434         /* identifiers */
1435         ot->name = "Move Layer";
1436         ot->description = "Move the active layer up/down in the list";
1437         ot->idname = "MASK_OT_layer_move";
1438
1439         /* api callbacks */
1440         ot->exec = mask_layer_move_exec;
1441         ot->poll = mask_layer_move_poll;
1442
1443         /* flags */
1444         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1445
1446         /* properties */
1447         RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Direction to move the active layer");
1448 }