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