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