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