mask merge (initial copy of editor files)
authorCampbell Barton <ideasman42@gmail.com>
Mon, 4 Jun 2012 15:36:16 +0000 (15:36 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 4 Jun 2012 15:36:16 +0000 (15:36 +0000)
source/blender/editors/mask/CMakeLists.txt [new file with mode: 0644]
source/blender/editors/mask/SConscript [new file with mode: 0644]
source/blender/editors/mask/mask_add.c [new file with mode: 0644]
source/blender/editors/mask/mask_draw.c [new file with mode: 0644]
source/blender/editors/mask/mask_edit.c [new file with mode: 0644]
source/blender/editors/mask/mask_intern.h [new file with mode: 0644]
source/blender/editors/mask/mask_ops.c [new file with mode: 0644]
source/blender/editors/mask/mask_relationships.c [new file with mode: 0644]
source/blender/editors/mask/mask_select.c [new file with mode: 0644]
source/blender/editors/mask/mask_shapekey.c [new file with mode: 0644]

diff --git a/source/blender/editors/mask/CMakeLists.txt b/source/blender/editors/mask/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f15e9c2
--- /dev/null
@@ -0,0 +1,51 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2012 Blender Foundation.
+#
+# Contributor(s): Blender Foundation,
+#                 Sergey Sharybin
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+       ../include
+       ../../blenkernel
+       ../../blenloader
+       ../../blenlib
+       ../../makesdna
+       ../../makesrna
+       ../../windowmanager
+       ../../../../intern/guardedalloc
+       ${GLEW_INCLUDE_PATH}
+)
+
+set(INC_SYS
+)
+
+set(SRC
+       mask_add.c
+       mask_draw.c
+       mask_edit.c
+       mask_ops.c
+       mask_relationships.c
+       mask_select.c
+       mask_shapekey.c
+
+       mask_intern.h
+)
+
+blender_add_lib(bf_editor_mask "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/mask/SConscript b/source/blender/editors/mask/SConscript
new file mode 100644 (file)
index 0000000..4af000d
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/python
+Import ('env')
+
+sources = env.Glob('*.c')
+defs = []
+incs = '../include ../../blenkernel ../../blenloader ../../blenlib ../../windowmanager ../../makesdna'
+incs += ' ../../makesrna #/extern/glew/include #/intern/guardedalloc'
+
+env.BlenderLib ( 'bf_editors_mask', sources, Split(incs), defs, libtype=['core'], priority=[100] )
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
new file mode 100644 (file)
index 0000000..c2c2ebb
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ *                 Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_add.c
+ *  \ingroup edmask
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_mask.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_mask_types.h"
+#include "DNA_object_types.h"  /* SELECT */
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_mask.h"
+#include "ED_clip.h"
+#include "ED_keyframing.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "mask_intern.h"  /* own include */
+
+
+static int find_nearest_diff_point(bContext *C, Mask *mask, const float normal_co[2], int threshold, int feather,
+                                   MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
+                                   float *u_r, float tangent[2])
+{
+       MaskLayer *masklay, *point_masklay;
+       MaskSpline *point_spline;
+       MaskSplinePoint *point = NULL;
+       float dist, co[2];
+       int width, height;
+       float u;
+       float scalex, scaley, aspx, aspy;
+
+       ED_mask_size(C, &width, &height);
+       ED_mask_aspect(C, &aspx, &aspy);
+       ED_mask_pixelspace_factor(C, &scalex, &scaley);
+
+       co[0] = normal_co[0] * scalex;
+       co[1] = normal_co[1] * scaley;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       int i;
+
+                       for (i = 0; i < spline->tot_point; i++) {
+                               MaskSplinePoint *cur_point = &spline->points[i];
+                               float *diff_points;
+                               int tot_diff_point;
+
+                               diff_points = BKE_mask_point_segment_diff_with_resolution(spline, cur_point, width, height,
+                                                                                         &tot_diff_point);
+
+                               if (diff_points) {
+                                       int i, tot_feather_point, tot_point;
+                                       float *feather_points = NULL, *points;
+
+                                       if (feather) {
+                                               feather_points = BKE_mask_point_segment_feather_diff_with_resolution(spline, cur_point,
+                                                                                                                    width, height,
+                                                                                                                    &tot_feather_point);
+
+                                               points = feather_points;
+                                               tot_point = tot_feather_point;
+                                       }
+                                       else {
+                                               points = diff_points;
+                                               tot_point = tot_diff_point;
+                                       }
+
+                                       for (i = 0; i < tot_point - 1; i++) {
+                                               float cur_dist, a[2], b[2];
+
+                                               a[0] = points[2 * i] * scalex;
+                                               a[1] = points[2 * i + 1] * scaley;
+
+                                               b[0] = points[2 * i + 2] * scalex;
+                                               b[1] = points[2 * i + 3] * scaley;
+
+                                               cur_dist = dist_to_line_segment_v2(co, a, b);
+
+                                               if (point == NULL || cur_dist < dist) {
+                                                       if (tangent)
+                                                               sub_v2_v2v2(tangent, &diff_points[2 * i + 2], &diff_points[2 * i]);
+
+                                                       point_masklay = masklay;
+                                                       point_spline = spline;
+                                                       point = cur_point;
+                                                       dist = cur_dist;
+                                                       u = (float)i / tot_point;
+
+                                               }
+                                       }
+
+                                       if (feather_points)
+                                               MEM_freeN(feather_points);
+
+                                       MEM_freeN(diff_points);
+                               }
+                       }
+               }
+       }
+
+       if (point && dist < threshold) {
+               if (masklay_r)
+                       *masklay_r = point_masklay;
+
+               if (spline_r)
+                       *spline_r = point_spline;
+
+               if (point_r)
+                       *point_r = point;
+
+               if (u_r) {
+                       u = BKE_mask_spline_project_co(point_spline, point, u, normal_co);
+
+                       *u_r = u;
+               }
+
+               return TRUE;
+       }
+
+       if (masklay_r)
+               *masklay_r = NULL;
+
+       if (spline_r)
+               *spline_r = NULL;
+
+       if (point_r)
+               *point_r = NULL;
+
+       return FALSE;
+}
+
+/******************** add vertex *********************/
+
+static void setup_vertex_point(bContext *C, Mask *mask, MaskSpline *spline, MaskSplinePoint *new_point,
+                               const float point_co[2], const float tangent[2], const float u,
+                               MaskSplinePoint *reference_point, const short reference_adjacent)
+{
+       MaskSplinePoint *prev_point = NULL;
+       MaskSplinePoint *next_point = NULL;
+       BezTriple *bezt;
+       int width, height;
+       float co[3];
+       const float len = 20.0; /* default length of handle in pixel space */
+
+       copy_v2_v2(co, point_co);
+       co[2] = 0.0f;
+
+       ED_mask_size(C, &width, &height);
+
+       /* point coordinate */
+       bezt = &new_point->bezt;
+
+       bezt->h1 = bezt->h2 = HD_ALIGN;
+
+       if (reference_point) {
+               bezt->h1 = bezt->h2 = MAX2(reference_point->bezt.h2, reference_point->bezt.h1);
+       }
+       else if (reference_adjacent) {
+               if (spline->tot_point != 1) {
+                       int index = (int)(new_point - spline->points);
+                       prev_point = &spline->points[(index - 1) % spline->tot_point];
+                       next_point = &spline->points[(index + 1) % spline->tot_point];
+
+                       bezt->h1 = bezt->h2 = MAX2(prev_point->bezt.h2, next_point->bezt.h1);
+
+                       /* note, we may want to copy other attributes later, radius? pressure? color? */
+               }
+       }
+
+       copy_v3_v3(bezt->vec[0], co);
+       copy_v3_v3(bezt->vec[1], co);
+       copy_v3_v3(bezt->vec[2], co);
+
+       /* initial offset for handles */
+       if (spline->tot_point == 1) {
+               /* first point of splien is aligned horizontally */
+               bezt->vec[0][0] -= len / width;
+               bezt->vec[2][0] += len / width;
+       }
+       else if (tangent) {
+               float vec[2];
+
+               copy_v2_v2(vec, tangent);
+
+               vec[0] *= width;
+               vec[1] *= height;
+
+               mul_v2_fl(vec, len / len_v2(vec));
+
+               vec[0] /= width;
+               vec[1] /= height;
+
+               sub_v2_v2(bezt->vec[0], vec);
+               add_v2_v2(bezt->vec[2], vec);
+
+               if (reference_adjacent) {
+                       BKE_mask_calc_handle_adjacent_interp(mask, spline, new_point, u);
+               }
+       }
+       else {
+
+               /* calculating auto handles works much nicer */
+#if 0
+               /* next points are aligning in the direction of previous/next point */
+               MaskSplinePoint *point;
+               float v1[2], v2[2], vec[2];
+               float dir = 1.0f;
+
+               if (new_point == spline->points) {
+                       point = new_point + 1;
+                       dir = -1.0f;
+               }
+               else
+                       point = new_point - 1;
+
+               if (spline->tot_point < 3) {
+                       v1[0] = point->bezt.vec[1][0] * width;
+                       v1[1] = point->bezt.vec[1][1] * height;
+
+                       v2[0] = new_point->bezt.vec[1][0] * width;
+                       v2[1] = new_point->bezt.vec[1][1] * height;
+               }
+               else {
+                       if (new_point == spline->points) {
+                               v1[0] = spline->points[1].bezt.vec[1][0] * width;
+                               v1[1] = spline->points[1].bezt.vec[1][1] * height;
+
+                               v2[0] = spline->points[spline->tot_point - 1].bezt.vec[1][0] * width;
+                               v2[1] = spline->points[spline->tot_point - 1].bezt.vec[1][1] * height;
+                       }
+                       else {
+                               v1[0] = spline->points[0].bezt.vec[1][0] * width;
+                               v1[1] = spline->points[0].bezt.vec[1][1] * height;
+
+                               v2[0] = spline->points[spline->tot_point - 2].bezt.vec[1][0] * width;
+                               v2[1] = spline->points[spline->tot_point - 2].bezt.vec[1][1] * height;
+                       }
+               }
+
+               sub_v2_v2v2(vec, v1, v2);
+               mul_v2_fl(vec, len * dir / len_v2(vec));
+
+               vec[0] /= width;
+               vec[1] /= height;
+
+               add_v2_v2(bezt->vec[0], vec);
+               sub_v2_v2(bezt->vec[2], vec);
+#else
+               BKE_mask_calc_handle_point_auto(mask, spline, new_point, TRUE);
+               BKE_mask_calc_handle_adjacent_interp(mask, spline, new_point, u);
+
+#endif
+       }
+
+       BKE_mask_parent_init(&new_point->parent);
+
+       /* select new point */
+       MASKPOINT_SEL_ALL(new_point);
+       ED_mask_select_flush_all(mask);
+}
+
+
+/* **** add extrude vertex **** */
+
+static void finSelectedSplinePoint(MaskLayer *masklay, MaskSpline **spline, MaskSplinePoint **point, short check_active)
+{
+       MaskSpline *cur_spline = masklay->splines.first;
+
+       *spline = NULL;
+       *point = NULL;
+
+       if (check_active) {
+               if (masklay->act_spline && masklay->act_point && MASKPOINT_ISSEL_ANY(masklay->act_point)) {
+                       *spline = masklay->act_spline;
+                       *point = masklay->act_point;
+                       return;
+               }
+       }
+
+       while (cur_spline) {
+               int i;
+
+               for (i = 0; i < cur_spline->tot_point; i++) {
+                       MaskSplinePoint *cur_point = &cur_spline->points[i];
+
+                       if (MASKPOINT_ISSEL_ANY(cur_point)) {
+                               if (*spline != NULL && *spline != cur_spline) {
+                                       *spline = NULL;
+                                       *point = NULL;
+                                       return;
+                               }
+                               else if (*point) {
+                                       *point = NULL;
+                               }
+                               else {
+                                       *spline = cur_spline;
+                                       *point = cur_point;
+                               }
+                       }
+               }
+
+               cur_spline = cur_spline->next;
+       }
+}
+
+/* **** add subdivide vertex **** */
+
+static void mask_spline_add_point_at_index(MaskSpline *spline, int point_index)
+{
+       MaskSplinePoint *new_point_array;
+
+       new_point_array = MEM_callocN(sizeof(MaskSplinePoint) * (spline->tot_point + 1), "add mask vert points");
+
+       memcpy(new_point_array, spline->points, sizeof(MaskSplinePoint) * (point_index + 1));
+       memcpy(new_point_array + point_index + 2, spline->points + point_index + 1,
+              sizeof(MaskSplinePoint) * (spline->tot_point - point_index - 1));
+
+       MEM_freeN(spline->points);
+       spline->points = new_point_array;
+       spline->tot_point++;
+}
+
+static int add_vertex_subdivide(bContext *C, Mask *mask, const float co[2])
+{
+       MaskLayer *masklay;
+       MaskSpline *spline;
+       MaskSplinePoint *point = NULL;
+       const float threshold = 9;
+       float tangent[2];
+       float u;
+
+       if (find_nearest_diff_point(C, mask, co, threshold, FALSE, &masklay, &spline, &point, &u, tangent)) {
+               MaskSplinePoint *new_point;
+               int point_index = point - spline->points;
+
+               ED_mask_select_toggle_all(mask, SEL_DESELECT);
+
+               mask_spline_add_point_at_index(spline, point_index);
+
+               new_point = &spline->points[point_index + 1];
+
+               setup_vertex_point(C, mask, spline, new_point, co, tangent, u, NULL, TRUE);
+
+               /* TODO - we could pass the spline! */
+               BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index + 1, TRUE, TRUE);
+
+               masklay->act_point = new_point;
+
+               WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static int add_vertex_extrude(bContext *C, Mask *mask, MaskLayer *masklay, const float co[2])
+{
+       MaskSpline *spline;
+       MaskSplinePoint *point;
+       MaskSplinePoint *new_point = NULL, *ref_point = NULL;
+
+       /* check on which side we want to add the point */
+       int point_index;
+       float tangent_point[2];
+       float tangent_co[2];
+       int do_cyclic_correct = FALSE;
+       int do_recalc_src = FALSE;  /* when extruding from endpoints only */
+       int do_prev;                /* use prev point rather then next?? */
+
+       if (!masklay) {
+               return FALSE;
+       }
+       else {
+               finSelectedSplinePoint(masklay, &spline, &point, TRUE);
+       }
+
+       ED_mask_select_toggle_all(mask, SEL_DESELECT);
+
+       point_index = (point - spline->points);
+
+       MASKPOINT_DESEL_ALL(point);
+
+       if ((spline->flag & MASK_SPLINE_CYCLIC) ||
+           (point_index > 0 && point_index != spline->tot_point - 1))
+       {
+               BKE_mask_calc_tangent_polyline(mask, spline, point, tangent_point);
+               sub_v2_v2v2(tangent_co, co, point->bezt.vec[1]);
+
+               if (dot_v2v2(tangent_point, tangent_co) < 0.0f) {
+                       do_prev = TRUE;
+               }
+               else {
+                       do_prev = FALSE;
+               }
+       }
+       else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == 0)) {
+               do_prev = TRUE;
+               do_recalc_src = TRUE;
+       }
+       else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == spline->tot_point - 1)) {
+               do_prev = FALSE;
+               do_recalc_src = TRUE;
+       }
+       else {
+               /* should never get here */
+               BLI_assert(0);
+       }
+
+       /* use the point before the active one */
+       if (do_prev) {
+               point_index--;
+               if (point_index < 0) {
+                       point_index += spline->tot_point; /* wrap index */
+                       if ((spline->flag & MASK_SPLINE_CYCLIC) == 0) {
+                               do_cyclic_correct = TRUE;
+                               point_index = 0;
+                       }
+               }
+       }
+
+//             print_v2("", tangent_point);
+//             printf("%d\n", point_index);
+
+       mask_spline_add_point_at_index(spline, point_index);
+
+       if (do_cyclic_correct) {
+               ref_point = &spline->points[point_index + 1];
+               new_point = &spline->points[point_index];
+               *ref_point = *new_point;
+               memset(new_point, 0, sizeof(*new_point));
+       }
+       else {
+               ref_point = &spline->points[point_index];
+               new_point = &spline->points[point_index + 1];
+       }
+
+       masklay->act_point = new_point;
+
+       setup_vertex_point(C, mask, spline, new_point, co, NULL, 0.5f, ref_point, FALSE);
+
+       if (masklay->splines_shapes.first) {
+               point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point);
+               BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, TRUE, TRUE);
+       }
+
+       if (do_recalc_src) {
+               /* TODO, update keyframes in time */
+               BKE_mask_calc_handle_point_auto(mask, spline, ref_point, FALSE);
+       }
+
+       WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+
+       return TRUE;
+}
+
+static int add_vertex_new(bContext *C, Mask *mask, MaskLayer *masklay, const float co[2])
+{
+       MaskSpline *spline;
+       MaskSplinePoint *point;
+       MaskSplinePoint *new_point = NULL, *ref_point = NULL;
+
+       if (!masklay) {
+               /* if there's no masklay currently operationg on, create new one */
+               masklay = BKE_mask_layer_new(mask, "");
+               mask->masklay_act = mask->masklay_tot - 1;
+               spline = NULL;
+               point = NULL;
+       }
+       else {
+               finSelectedSplinePoint(masklay, &spline, &point, TRUE);
+       }
+
+       ED_mask_select_toggle_all(mask, SEL_DESELECT);
+
+       if (!spline) {
+               /* no selected splines in active masklay, create new spline */
+               spline = BKE_mask_spline_add(masklay);
+       }
+
+       masklay->act_spline = spline;
+       new_point = spline->points;
+
+       masklay->act_point = new_point;
+
+       setup_vertex_point(C, mask, spline, new_point, co, NULL, 0.5f, ref_point, FALSE);
+
+       {
+               int point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point);
+               BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, TRUE, TRUE);
+       }
+
+       WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+
+       return TRUE;
+}
+
+static int add_vertex_exec(bContext *C, wmOperator *op)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+
+       float co[2];
+
+       masklay = BKE_mask_layer_active(mask);
+
+       if (masklay && masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+               masklay = NULL;
+       }
+
+       RNA_float_get_array(op->ptr, "location", co);
+
+       if (masklay && masklay->act_point && MASKPOINT_ISSEL_ANY(masklay->act_point)) {
+
+               /* cheap trick - double click for cyclic */
+               MaskSpline *spline = masklay->act_spline;
+               MaskSplinePoint *point = masklay->act_point;
+
+               int is_sta = (point == spline->points);
+               int is_end = (point == &spline->points[spline->tot_point - 1]);
+
+               /* then check are we overlapping the mouse */
+               if ((is_sta || is_end) && equals_v2v2(co, point->bezt.vec[1])) {
+                       if (spline->flag & MASK_SPLINE_CYCLIC) {
+                               /* nothing to do */
+                               return OPERATOR_CANCELLED;
+                       }
+                       else {
+                               /* recalc the connecting point as well to make a nice even curve */
+                               MaskSplinePoint *point_other = is_end ? spline->points : &spline->points[spline->tot_point - 1];
+                               spline->flag |= MASK_SPLINE_CYCLIC;
+
+                               /* TODO, update keyframes in time */
+                               BKE_mask_calc_handle_point_auto(mask, spline, point, FALSE);
+                               BKE_mask_calc_handle_point_auto(mask, spline, point_other, FALSE);
+
+                               /* TODO: only update this spline */
+                               BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
+
+                               WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+                               return OPERATOR_FINISHED;
+                       }
+               }
+
+               if (!add_vertex_subdivide(C, mask, co)) {
+                       if (!add_vertex_extrude(C, mask, masklay, co)) {
+                               return OPERATOR_CANCELLED;
+                       }
+               }
+       }
+       else {
+               if (!add_vertex_subdivide(C, mask, co)) {
+                       if (!add_vertex_new(C, mask, masklay, co)) {
+                               return OPERATOR_CANCELLED;
+                       }
+               }
+       }
+
+       /* TODO: only update this spline */
+       BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
+
+       return OPERATOR_FINISHED;
+}
+
+static int add_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       float co[2];
+
+       ED_mask_mouse_pos(C, event, co);
+
+       RNA_float_set_array(op->ptr, "location", co);
+
+       return add_vertex_exec(C, op);
+}
+
+void MASK_OT_add_vertex(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Add Vertex";
+       ot->description = "Add vertex to active spline";
+       ot->idname = "MASK_OT_add_vertex";
+
+       /* api callbacks */
+       ot->exec = add_vertex_exec;
+       ot->invoke = add_vertex_invoke;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* properties */
+       RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
+                            "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
+}
+
+/******************** add feather vertex *********************/
+
+static int add_feather_vertex_exec(bContext *C, wmOperator *op)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       MaskSpline *spline;
+       MaskSplinePoint *point = NULL;
+       const float threshold = 9;
+       float co[2], u;
+
+       RNA_float_get_array(op->ptr, "location", co);
+
+       point = ED_mask_point_find_nearest(C, mask, co, threshold, NULL, NULL, NULL, NULL);
+       if (point)
+               return OPERATOR_FINISHED;
+
+       if (find_nearest_diff_point(C, mask, co, threshold, TRUE, &masklay, &spline, &point, &u, NULL)) {
+               Scene *scene = CTX_data_scene(C);
+               float w = BKE_mask_point_weight(spline, point, u);
+
+               BKE_mask_point_add_uw(point, u, w);
+
+               BKE_mask_update_display(mask, scene->r.cfra);
+
+               WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+
+               DAG_id_tag_update(&mask->id, 0);
+
+               return OPERATOR_FINISHED;
+       }
+
+       return OPERATOR_CANCELLED;
+}
+
+static int add_feather_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       float co[2];
+
+       ED_mask_mouse_pos(C, event, co);
+
+       RNA_float_set_array(op->ptr, "location", co);
+
+       return add_feather_vertex_exec(C, op);
+}
+
+void MASK_OT_add_feather_vertex(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Add feather Vertex";
+       ot->description = "Add vertex to feather";
+       ot->idname = "MASK_OT_add_feather_vertex";
+
+       /* api callbacks */
+       ot->exec = add_feather_vertex_exec;
+       ot->invoke = add_feather_vertex_invoke;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* properties */
+       RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
+                            "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
+}
diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c
new file mode 100644 (file)
index 0000000..e580088
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ *                 Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_draw.c
+ *  \ingroup edmask
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_mask.h"
+
+#include "DNA_mask_types.h"
+#include "DNA_object_types.h"   /* SELECT */
+
+#include "ED_mask.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "UI_resources.h"
+
+#include "mask_intern.h"  /* own include */
+
+static void mask_spline_color_get(MaskLayer *masklay, MaskSpline *spline, const int is_sel,
+                                  unsigned char r_rgb[4])
+{
+       if (is_sel) {
+               if (masklay->act_spline == spline) {
+                       r_rgb[0] = r_rgb[1] = r_rgb[2] = 255;
+               }
+               else {
+                       r_rgb[0] = 255;
+                       r_rgb[1] = r_rgb[2] = 0;
+               }
+       }
+       else {
+               r_rgb[0] = 128;
+               r_rgb[1] = r_rgb[2] = 0;
+       }
+
+       r_rgb[3] = 255;
+}
+
+static void mask_spline_feather_color_get(MaskLayer *UNUSED(masklay), MaskSpline *UNUSED(spline), const int is_sel,
+                                          unsigned char r_rgb[4])
+{
+       if (is_sel) {
+               r_rgb[1] = 255;
+               r_rgb[0] = r_rgb[2] = 0;
+       }
+       else {
+               r_rgb[1] = 128;
+               r_rgb[0] = r_rgb[2] = 0;
+       }
+
+       r_rgb[3] = 255;
+}
+
+#if 0
+static void draw_spline_parents(MaskLayer *UNUSED(masklay), MaskSpline *spline)
+{
+       int i;
+       MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
+
+       if (!spline->tot_point)
+               return;
+
+       glColor3ub(0, 0, 0);
+       glEnable(GL_LINE_STIPPLE);
+       glLineStipple(1, 0xAAAA);
+
+       glBegin(GL_LINES);
+
+       for (i = 0; i < spline->tot_point; i++) {
+               MaskSplinePoint *point = &points_array[i];
+               BezTriple *bezt = &point->bezt;
+
+               if (point->parent.flag & MASK_PARENT_ACTIVE) {
+                       glVertex2f(bezt->vec[1][0],
+                                  bezt->vec[1][1]);
+
+                       glVertex2f(bezt->vec[1][0] - point->parent.offset[0],
+                                  bezt->vec[1][1] - point->parent.offset[1]);
+               }
+       }
+
+       glEnd();
+
+       glDisable(GL_LINE_STIPPLE);
+}
+#endif
+
+/* return non-zero if spline is selected */
+static void draw_spline_points(MaskLayer *masklay, MaskSpline *spline)
+{
+       const int is_spline_sel = (spline->flag & SELECT) && (masklay->restrictflag & MASK_RESTRICT_SELECT) == 0;
+       unsigned char rgb_spline[4];
+       MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
+
+       int i, hsize, tot_feather_point;
+       float (*feather_points)[2], (*fp)[2];
+
+       if (!spline->tot_point)
+               return;
+
+       hsize = UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE);
+
+       glPointSize(hsize);
+
+       mask_spline_color_get(masklay, spline, is_spline_sel, rgb_spline);
+
+       /* feather points */
+       feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
+       for (i = 0; i < spline->tot_point; i++) {
+
+               /* watch it! this is intentionally not the deform array, only check for sel */
+               MaskSplinePoint *point = &spline->points[i];
+
+               int j;
+
+               for (j = 0; j < point->tot_uw + 1; j++) {
+                       int sel = FALSE;
+
+                       if (j == 0) {
+                               sel = MASKPOINT_ISSEL_ANY(point);
+                       }
+                       else {
+                               sel = point->uw[j - 1].flag & SELECT;
+                       }
+
+                       if (sel) {
+                               if (point == masklay->act_point)
+                                       glColor3f(1.0f, 1.0f, 1.0f);
+                               else
+                                       glColor3f(1.0f, 1.0f, 0.0f);
+                       }
+                       else {
+                               glColor3f(0.5f, 0.5f, 0.0f);
+                       }
+
+                       glBegin(GL_POINTS);
+                       glVertex2fv(*fp);
+                       glEnd();
+
+                       fp++;
+               }
+       }
+       MEM_freeN(feather_points);
+
+       /* control points */
+       for (i = 0; i < spline->tot_point; i++) {
+
+               /* watch it! this is intentionally not the deform array, only check for sel */
+               MaskSplinePoint *point = &spline->points[i];
+               MaskSplinePoint *point_deform = &points_array[i];
+               BezTriple *bezt = &point_deform->bezt;
+
+               float handle[2];
+               float *vert = bezt->vec[1];
+               int has_handle = BKE_mask_point_has_handle(point);
+
+               BKE_mask_point_handle(point_deform, handle);
+
+               /* draw handle segment */
+               if (has_handle) {
+                       glColor3ubv(rgb_spline);
+
+                       glBegin(GL_LINES);
+                       glVertex3fv(vert);
+                       glVertex3fv(handle);
+                       glEnd();
+               }
+
+               /* draw CV point */
+               if (MASKPOINT_ISSEL_KNOT(point)) {
+                       if (point == masklay->act_point)
+                               glColor3f(1.0f, 1.0f, 1.0f);
+                       else
+                               glColor3f(1.0f, 1.0f, 0.0f);
+               }
+               else
+                       glColor3f(0.5f, 0.5f, 0.0f);
+
+               glBegin(GL_POINTS);
+               glVertex3fv(vert);
+               glEnd();
+
+               /* draw handle points */
+               if (has_handle) {
+                       if (MASKPOINT_ISSEL_HANDLE(point)) {
+                               if (point == masklay->act_point)
+                                       glColor3f(1.0f, 1.0f, 1.0f);
+                               else
+                                       glColor3f(1.0f, 1.0f, 0.0f);
+                       }
+                       else {
+                               glColor3f(0.5f, 0.5f, 0.0f);
+                       }
+
+                       glBegin(GL_POINTS);
+                       glVertex3fv(handle);
+                       glEnd();
+               }
+       }
+
+       glPointSize(1.0f);
+}
+
+/* #define USE_XOR */
+
+static void mask_draw_curve_type(MaskSpline *spline, float (*points)[2], int tot_point,
+                                 const short is_feather, const short is_smooth,
+                                 const unsigned char rgb_spline[4], const char draw_type)
+{
+       const int draw_method = (spline->flag & MASK_SPLINE_CYCLIC) ? GL_LINE_LOOP : GL_LINE_STRIP;
+       unsigned char rgb_tmp[4];
+
+       glEnableClientState(GL_VERTEX_ARRAY);
+       glVertexPointer(2, GL_FLOAT, 0, points);
+
+       switch (draw_type) {
+
+               case MASK_DT_OUTLINE:
+                       glLineWidth(3);
+                       cpack(0x0);
+
+                       glDrawArrays(draw_method, 0, tot_point);
+
+                       glLineWidth(1);
+                       glColor4ubv(rgb_spline);
+                       glDrawArrays(draw_method, 0, tot_point);
+
+                       break;
+
+               case MASK_DT_DASH:
+               default:
+                       glEnable(GL_LINE_STIPPLE);
+
+#ifdef USE_XOR
+                       glEnable(GL_COLOR_LOGIC_OP);
+                       glLogicOp(GL_OR);
+#endif
+                       glColor4ubv(rgb_spline);
+                       glLineStipple(3, 0xaaaa);
+                       glEnableClientState(GL_VERTEX_ARRAY);
+                       glVertexPointer(2, GL_FLOAT, 0, points);
+                       glDrawArrays(draw_method, 0, tot_point);
+
+#ifdef USE_XOR
+                       glDisable(GL_COLOR_LOGIC_OP);
+#endif
+                       glColor4ub(0, 0, 0, 255);
+                       glLineStipple(3, 0x5555);
+                       glDrawArrays(draw_method, 0, tot_point);
+
+                       glDisable(GL_LINE_STIPPLE);
+                       break;
+
+
+               case MASK_DT_BLACK:
+               case MASK_DT_WHITE:
+                       if (draw_type == MASK_DT_BLACK) { rgb_tmp[0] = rgb_tmp[1] = rgb_tmp[2] = 0;   }
+                       else                            { rgb_tmp[0] = rgb_tmp[1] = rgb_tmp[2] = 255; }
+                       /* alpha values seem too low but gl draws many points that compensate for it */
+                       if (is_feather) { rgb_tmp[3] = 64; }
+                       else            { rgb_tmp[3] = 128; }
+
+                       if (is_feather) {
+                               rgb_tmp[0] = (unsigned char)(((short)rgb_tmp[0] + (short)rgb_spline[0]) / 2);
+                               rgb_tmp[1] = (unsigned char)(((short)rgb_tmp[1] + (short)rgb_spline[1]) / 2);
+                               rgb_tmp[2] = (unsigned char)(((short)rgb_tmp[2] + (short)rgb_spline[2]) / 2);
+                       }
+
+                       if (is_smooth == FALSE && is_feather) {
+                               glEnable(GL_BLEND);
+                               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                       }
+
+                       glColor4ubv(rgb_tmp);
+                       glEnableClientState(GL_VERTEX_ARRAY);
+                       glVertexPointer(2, GL_FLOAT, 0, points);
+                       glDrawArrays(draw_method, 0, tot_point);
+
+                       glDrawArrays(draw_method, 0, tot_point);
+
+                       if (is_smooth == FALSE && is_feather) {
+                               glDisable(GL_BLEND);
+                       }
+
+                       break;
+       }
+
+       glDisableClientState(GL_VERTEX_ARRAY);
+
+}
+
+static void draw_spline_curve(MaskLayer *masklay, MaskSpline *spline,
+                              const char draw_flag, const char draw_type,
+                              int width, int height)
+{
+       unsigned char rgb_tmp[4];
+
+       const short is_spline_sel = (spline->flag & SELECT) && (masklay->restrictflag & MASK_RESTRICT_SELECT) == 0;
+       const short is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH);
+
+       int tot_diff_point;
+       float (*diff_points)[2];
+
+       int tot_feather_point;
+       float (*feather_points)[2];
+
+       diff_points = BKE_mask_spline_differentiate_with_resolution(spline, width, height, &tot_diff_point);
+
+       if (!diff_points)
+               return;
+
+       if (is_smooth) {
+               glEnable(GL_LINE_SMOOTH);
+               glEnable(GL_BLEND);
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+       }
+
+       feather_points = BKE_mask_spline_feather_differentiated_points_with_resolution(spline, width, height, &tot_feather_point);
+
+       /* draw feather */
+       mask_spline_feather_color_get(masklay, spline, is_spline_sel, rgb_tmp);
+       mask_draw_curve_type(spline, feather_points, tot_feather_point,
+                            TRUE, is_smooth,
+                            rgb_tmp, draw_type);
+       MEM_freeN(feather_points);
+
+       /* draw main curve */
+       mask_spline_color_get(masklay, spline, is_spline_sel, rgb_tmp);
+       mask_draw_curve_type(spline, diff_points, tot_diff_point,
+                            FALSE, is_smooth,
+                            rgb_tmp, draw_type);
+       MEM_freeN(diff_points);
+
+       if (draw_flag & MASK_DRAWFLAG_SMOOTH) {
+               glDisable(GL_LINE_SMOOTH);
+               glDisable(GL_BLEND);
+       }
+
+       (void)draw_type;
+}
+
+static void draw_masklays(Mask *mask, const char draw_flag, const char draw_type,
+                          int width, int height)
+{
+       MaskLayer *masklay;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+
+                       /* draw curve itself first... */
+                       draw_spline_curve(masklay, spline, draw_flag, draw_type, width, height);
+
+//                     draw_spline_parents(masklay, spline);
+
+                       if (!(masklay->restrictflag & MASK_RESTRICT_SELECT)) {
+                               /* ...and then handles over the curve so they're nicely visible */
+                               draw_spline_points(masklay, spline);
+                       }
+
+                       /* show undeform for testing */
+                       if (0) {
+                               void *back = spline->points_deform;
+
+                               spline->points_deform = NULL;
+                               draw_spline_curve(masklay, spline, draw_flag, draw_type, width, height);
+//                             draw_spline_parents(masklay, spline);
+                               draw_spline_points(masklay, spline);
+                               spline->points_deform = back;
+                       }
+               }
+       }
+}
+
+void ED_mask_draw(const bContext *C,
+                  const char draw_flag, const char draw_type)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       int width, height;
+
+       if (!mask)
+               return;
+
+       /* TODO: for now, in the future better to make sure all utility functions
+        *       are using const specifier for non-changing pointers
+        */
+       ED_mask_size((bContext *)C, &width, &height);
+
+       draw_masklays(mask, draw_flag, draw_type, width, height);
+}
diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c
new file mode 100644 (file)
index 0000000..0b8551d
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ *                 Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_ops.c
+ *  \ingroup edmask
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_mask.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_mask.h"
+#include "ED_object.h" /* ED_keymap_proportional_maskmode only */
+#include "ED_clip.h"
+#include "ED_transform.h"
+
+#include "RNA_access.h"
+
+#include "mask_intern.h"  /* own include */
+
+/********************** generic poll functions *********************/
+
+int ED_maskediting_poll(bContext *C)
+{
+       SpaceClip *sc = CTX_wm_space_clip(C);
+
+       if (sc) {
+               return ED_space_clip_maskediting_poll(C);
+       }
+
+       return FALSE;
+}
+
+int ED_maskediting_mask_poll(bContext *C)
+{
+       SpaceClip *sc = CTX_wm_space_clip(C);
+
+       if (sc) {
+               return ED_space_clip_maskediting_mask_poll(C);
+       }
+
+       return FALSE;
+}
+
+/********************** registration *********************/
+
+void ED_mask_mouse_pos(bContext *C, wmEvent *event, float co[2])
+{
+       SpaceClip *sc = CTX_wm_space_clip(C);
+
+       if (sc) {
+               ED_clip_mouse_pos(C, event, co);
+               BKE_mask_coord_from_movieclip(sc->clip, &sc->user, co, co);
+       }
+       else {
+               /* possible other spaces from which mask editing is available */
+               zero_v2(co);
+       }
+}
+
+/* input:  x/y   - mval space
+ * output: xr/yr - mask point space */
+void ED_mask_point_pos(bContext *C, float x, float y, float *xr, float *yr)
+{
+       SpaceClip *sc = CTX_wm_space_clip(C);
+       float co[2];
+
+       if (sc) {
+               ED_clip_point_stable_pos(C, x, y, &co[0], &co[1]);
+               BKE_mask_coord_from_movieclip(sc->clip, &sc->user, co, co);
+       }
+       else {
+               /* possible other spaces from which mask editing is available */
+               zero_v2(co);
+       }
+
+       *xr = co[0];
+       *yr = co[1];
+}
+
+void ED_mask_point_pos__reverse(bContext *C, float x, float y, float *xr, float *yr)
+{
+       SpaceClip *sc = CTX_wm_space_clip(C);
+       ARegion *ar = CTX_wm_region(C);
+
+       float co[2];
+
+       if (sc && ar) {
+               co[0] = x;
+               co[1] = y;
+               BKE_mask_coord_to_movieclip(sc->clip, &sc->user, co, co);
+               ED_clip_point_stable_pos__reverse(sc, ar, co, co);
+       }
+       else {
+               /* possible other spaces from which mask editing is available */
+               zero_v2(co);
+       }
+
+       *xr = co[0];
+       *yr = co[1];
+}
+
+void ED_mask_size(bContext *C, int *width, int *height)
+{
+       SpaceClip *sc = CTX_wm_space_clip(C);
+
+       if (sc) {
+               ED_space_clip_mask_size(sc, width, height);
+       }
+       else {
+               /* possible other spaces from which mask editing is available */
+               *width = 0;
+               *height = 0;
+       }
+}
+
+void ED_mask_aspect(bContext *C, float *aspx, float *aspy)
+{
+       SpaceClip *sc = CTX_wm_space_clip(C);
+
+       if (sc) {
+               ED_space_clip_mask_aspect(sc, aspx, aspy);
+       }
+       else {
+               /* possible other spaces from which mask editing is available */
+               *aspx = 1.0f;
+               *aspy = 1.0f;
+       }
+}
+
+void ED_mask_pixelspace_factor(bContext *C, float *scalex, float *scaley)
+{
+       SpaceClip *sc = CTX_wm_space_clip(C);
+
+       if (sc) {
+               ARegion *ar = CTX_wm_region(C);
+               int width, height;
+               float zoomx, zoomy, aspx, aspy;
+
+               ED_space_clip_size(sc, &width, &height);
+               ED_space_clip_zoom(sc, ar, &zoomx, &zoomy);
+               ED_space_clip_aspect(sc, &aspx, &aspy);
+
+               *scalex = ((float)width * aspx) * zoomx;
+               *scaley = ((float)height * aspy) * zoomy;
+       }
+       else {
+               /* possible other spaces from which mask editing is available */
+               *scalex = 1.0f;
+               *scaley = 1.0f;
+       }
+}
+
+/********************** registration *********************/
+
+void ED_operatortypes_mask(void)
+{
+       WM_operatortype_append(MASK_OT_new);
+
+       /* mask layers */
+       WM_operatortype_append(MASK_OT_layer_new);
+       WM_operatortype_append(MASK_OT_layer_remove);
+
+       /* add */
+       WM_operatortype_append(MASK_OT_add_vertex);
+       WM_operatortype_append(MASK_OT_add_feather_vertex);
+
+       /* geometry */
+       WM_operatortype_append(MASK_OT_switch_direction);
+       WM_operatortype_append(MASK_OT_delete);
+
+       /* select */
+       WM_operatortype_append(MASK_OT_select);
+       WM_operatortype_append(MASK_OT_select_all);
+       WM_operatortype_append(MASK_OT_select_border);
+       WM_operatortype_append(MASK_OT_select_lasso);
+       WM_operatortype_append(MASK_OT_select_circle);
+       WM_operatortype_append(MASK_OT_select_linked_pick);
+       WM_operatortype_append(MASK_OT_select_linked);
+
+       /* hide/reveal */
+       WM_operatortype_append(MASK_OT_hide_view_clear);
+       WM_operatortype_append(MASK_OT_hide_view_set);
+
+       /* shape */
+       WM_operatortype_append(MASK_OT_slide_point);
+       WM_operatortype_append(MASK_OT_cyclic_toggle);
+       WM_operatortype_append(MASK_OT_handle_type_set);
+
+       /* relationships */
+       WM_operatortype_append(MASK_OT_parent_set);
+       WM_operatortype_append(MASK_OT_parent_clear);
+
+       /* shapekeys */
+       WM_operatortype_append(MASK_OT_shape_key_insert);
+       WM_operatortype_append(MASK_OT_shape_key_clear);
+}
+
+void ED_keymap_mask(wmKeyConfig *keyconf)
+{
+       wmKeyMap *keymap;
+       wmKeyMapItem *kmi;
+
+       keymap = WM_keymap_find(keyconf, "Mask Editor", 0, 0);
+       keymap->poll = ED_maskediting_poll;
+
+       WM_keymap_add_item(keymap, "MASK_OT_new", NKEY, KM_PRESS, KM_ALT, 0);
+
+       /* mask mode supports PET now */
+       ED_keymap_proportional_cycle(keyconf, keymap);
+       ED_keymap_proportional_maskmode(keyconf, keymap);
+
+       /* geometry */
+       WM_keymap_add_item(keymap, "MASK_OT_add_vertex_slide", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
+       WM_keymap_add_item(keymap, "MASK_OT_add_feather_vertex_slide", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
+       WM_keymap_add_item(keymap, "MASK_OT_delete", XKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "MASK_OT_delete", DELKEY, KM_PRESS, 0, 0);
+
+       /* selection */
+       kmi = WM_keymap_add_item(keymap, "MASK_OT_select", SELECTMOUSE, KM_PRESS, 0, 0);
+       RNA_boolean_set(kmi->ptr, "extend", FALSE);
+       RNA_boolean_set(kmi->ptr, "deselect", FALSE);
+       RNA_boolean_set(kmi->ptr, "toggle", FALSE);
+       kmi = WM_keymap_add_item(keymap, "MASK_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0);
+       RNA_boolean_set(kmi->ptr, "extend", FALSE);
+       RNA_boolean_set(kmi->ptr, "deselect", FALSE);
+       RNA_boolean_set(kmi->ptr, "toggle", TRUE);
+
+       kmi = WM_keymap_add_item(keymap, "MASK_OT_select_all", AKEY, KM_PRESS, 0, 0);
+       RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
+       kmi = WM_keymap_add_item(keymap, "MASK_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
+       RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
+
+       WM_keymap_add_item(keymap, "MASK_OT_select_linked", LKEY, KM_PRESS, KM_CTRL, 0);
+       kmi = WM_keymap_add_item(keymap, "MASK_OT_select_linked_pick", LKEY, KM_PRESS, 0, 0);
+       RNA_boolean_set(kmi->ptr, "deselect", FALSE);
+       kmi = WM_keymap_add_item(keymap, "MASK_OT_select_linked_pick", LKEY, KM_PRESS, KM_SHIFT, 0);
+       RNA_boolean_set(kmi->ptr, "deselect", TRUE);
+
+       WM_keymap_add_item(keymap, "MASK_OT_select_border", BKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "MASK_OT_select_circle", CKEY, KM_PRESS, 0, 0);
+
+       kmi = WM_keymap_add_item(keymap, "MASK_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0);
+       RNA_boolean_set(kmi->ptr, "deselect", FALSE);
+       kmi = WM_keymap_add_item(keymap, "MASK_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_SHIFT | KM_ALT, 0);
+       RNA_boolean_set(kmi->ptr, "deselect", TRUE);
+
+       /* hide/reveal */
+       WM_keymap_add_item(keymap, "MASK_OT_hide_view_clear", HKEY, KM_PRESS, KM_ALT, 0);
+       kmi = WM_keymap_add_item(keymap, "MASK_OT_hide_view_set", HKEY, KM_PRESS, 0, 0);
+       RNA_boolean_set(kmi->ptr, "unselected", FALSE);
+
+       kmi = WM_keymap_add_item(keymap, "MASK_OT_hide_view_set", HKEY, KM_PRESS, KM_SHIFT, 0);
+       RNA_boolean_set(kmi->ptr, "unselected", TRUE);
+
+       /* select clip while in maker view,
+        * this matches View3D functionality where you can select an
+        * object while in editmode to allow vertex parenting */
+       kmi = WM_keymap_add_item(keymap, "CLIP_OT_select", SELECTMOUSE, KM_PRESS, KM_CTRL, 0);
+       RNA_boolean_set(kmi->ptr, "extend", FALSE);
+
+       /* shape */
+       WM_keymap_add_item(keymap, "MASK_OT_cyclic_toggle", CKEY, KM_PRESS, KM_ALT, 0);
+       WM_keymap_add_item(keymap, "MASK_OT_slide_point", LEFTMOUSE, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "MASK_OT_handle_type_set", VKEY, KM_PRESS, 0, 0);
+
+       /* relationships */
+       WM_keymap_add_item(keymap, "MASK_OT_parent_set", PKEY, KM_PRESS, KM_CTRL, 0);
+       WM_keymap_add_item(keymap, "MASK_OT_parent_clear", PKEY, KM_PRESS, KM_ALT, 0);
+
+       WM_keymap_add_item(keymap, "MASK_OT_shape_key_insert", IKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_item(keymap, "MASK_OT_shape_key_clear", IKEY, KM_PRESS, KM_ALT, 0);
+
+
+       transform_keymap_for_space(keyconf, keymap, SPACE_CLIP);
+}
+
+void ED_operatormacros_mask(void)
+{
+       /* XXX: just for sample */
+       wmOperatorType *ot;
+       wmOperatorTypeMacro *otmacro;
+
+       ot = WM_operatortype_append_macro("MASK_OT_add_vertex_slide", "Add Vertex and Slide", "Add new vertex and slide it", OPTYPE_UNDO | OPTYPE_REGISTER);
+       ot->description = "Add new vertex and slide it";
+       WM_operatortype_macro_define(ot, "MASK_OT_add_vertex");
+       otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+       RNA_boolean_set(otmacro->ptr, "release_confirm", TRUE);
+
+       ot = WM_operatortype_append_macro("MASK_OT_add_feather_vertex_slide", "Add Feather Vertex and Slide", "Add new vertex to feater and slide it", OPTYPE_UNDO | OPTYPE_REGISTER);
+       ot->description = "Add new feather vertex and slide it";
+       WM_operatortype_macro_define(ot, "MASK_OT_add_feather_vertex");
+       otmacro = WM_operatortype_macro_define(ot, "MASK_OT_slide_point");
+       RNA_boolean_set(otmacro->ptr, "slide_feather", TRUE);
+}
diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h
new file mode 100644 (file)
index 0000000..70f13ab
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2011 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ *                 Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_intern.h
+ *  \ingroup spclip
+ */
+
+#ifndef __MASK_INTERN_H__
+#define __MASK_INTERN_H__
+
+struct bContext;
+struct wmEvent;
+struct wmOperatorType;
+
+/* internal exports only */
+
+/* mask_add.c */
+void MASK_OT_add_vertex(struct wmOperatorType *ot);
+void MASK_OT_add_feather_vertex(struct wmOperatorType *ot);
+
+/* mask_ops.c */
+void MASK_OT_new(struct wmOperatorType *ot);
+void MASK_OT_layer_new(struct wmOperatorType *ot);
+void MASK_OT_layer_remove(struct wmOperatorType *ot);
+void MASK_OT_cyclic_toggle(struct wmOperatorType *ot);
+
+void MASK_OT_slide_point(struct wmOperatorType *ot);
+
+void MASK_OT_delete(struct wmOperatorType *ot);
+
+void MASK_OT_hide_view_clear(struct wmOperatorType *ot);
+void MASK_OT_hide_view_set(struct wmOperatorType *ot);
+void MASK_OT_switch_direction(struct wmOperatorType *ot);
+
+void MASK_OT_handle_type_set(struct wmOperatorType *ot);
+
+int ED_mask_feather_find_nearest(
+        struct bContext *C, struct Mask *mask, float normal_co[2], int threshold,
+        struct MaskLayer **masklay_r, struct MaskSpline **spline_r, struct MaskSplinePoint **point_r,
+        struct MaskSplinePointUW **uw_r, float *score);
+
+struct MaskSplinePoint *ED_mask_point_find_nearest(
+        struct bContext *C, struct Mask *mask, float normal_co[2], int threshold,
+        struct MaskLayer **masklay_r, struct MaskSpline **spline_r, int *is_handle_r,
+        float *score);
+
+/* mask_relationships.c */
+void MASK_OT_parent_set(struct wmOperatorType *ot);
+void MASK_OT_parent_clear(struct wmOperatorType *ot);
+
+/* mask_select.c */
+void MASK_OT_select(struct wmOperatorType *ot);
+void MASK_OT_select_all(struct wmOperatorType *ot);
+
+void MASK_OT_select_border(struct wmOperatorType *ot);
+void MASK_OT_select_lasso(struct wmOperatorType *ot);
+void MASK_OT_select_circle(struct wmOperatorType *ot);
+void MASK_OT_select_linked_pick(struct wmOperatorType *ot);
+void MASK_OT_select_linked(struct wmOperatorType *ot);
+
+int ED_mask_spline_select_check(struct MaskSpline *spline);
+int ED_mask_layer_select_check(struct MaskLayer *masklay);
+int ED_mask_select_check(struct Mask *mask);
+
+void ED_mask_spline_select_set(struct MaskSpline *spline, const short do_select);
+void ED_mask_layer_select_set(struct MaskLayer *masklay, const short do_select);
+void ED_mask_select_toggle_all(struct Mask *mask, int action);
+void ED_mask_select_flush_all(struct Mask *mask);
+
+/* mask_editor.c */
+int ED_maskediting_poll(struct bContext *C);
+int ED_maskediting_mask_poll(struct bContext *C);
+
+void ED_mask_size(struct bContext *C, int *width, int *height);
+void ED_mask_aspect(struct bContext *C, float *aspx, float *aspy);
+
+void ED_mask_pixelspace_factor(struct bContext *C, float *scalex, float *scaley);
+void ED_mask_mouse_pos(struct bContext *C, struct wmEvent *event, float co[2]);
+
+void ED_mask_point_pos(struct bContext *C, float x, float y, float *xr, float *yr);
+void ED_mask_point_pos__reverse(struct bContext *C, float x, float y, float *xr, float *yr);
+
+/* mask_shapekey.c */
+void MASK_OT_shape_key_insert(struct wmOperatorType *ot);
+void MASK_OT_shape_key_clear(struct wmOperatorType *ot);
+
+#endif /* __MASK_INTERN_H__ */
diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c
new file mode 100644 (file)
index 0000000..cf3a72b
--- /dev/null
@@ -0,0 +1,1171 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ *                 Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_ops.c
+ *  \ingroup edmask
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_mask.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_mask_types.h"
+#include "DNA_object_types.h"  /* SELECT */
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_mask.h"
+#include "ED_clip.h"
+#include "ED_keyframing.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "mask_intern.h"  /* own include */
+
+/******************** utility functions *********************/
+
+MaskSplinePoint *ED_mask_point_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold,
+                                            MaskLayer **masklay_r, MaskSpline **spline_r, int *is_handle_r,
+                                            float *score)
+{
+       MaskLayer *masklay;
+       MaskLayer *point_masklay = NULL;
+       MaskSpline *point_spline = NULL;
+       MaskSplinePoint *point = NULL;
+       float co[2], aspx, aspy;
+       float len = FLT_MAX, scalex, scaley;
+       int is_handle = FALSE, width, height;
+
+       ED_mask_size(C, &width, &height);
+       ED_mask_aspect(C, &aspx, &aspy);
+       ED_mask_pixelspace_factor(C, &scalex, &scaley);
+
+       co[0] = normal_co[0] * scalex;
+       co[1] = normal_co[1] * scaley;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
+
+                       int i;
+
+                       for (i = 0; i < spline->tot_point; i++) {
+                               MaskSplinePoint *cur_point = &spline->points[i];
+                               MaskSplinePoint *cur_point_deform = &points_array[i];
+                               float cur_len, vec[2], handle[2];
+
+                               vec[0] = cur_point_deform->bezt.vec[1][0] * scalex;
+                               vec[1] = cur_point_deform->bezt.vec[1][1] * scaley;
+
+                               if (BKE_mask_point_has_handle(cur_point)) {
+                                       BKE_mask_point_handle(cur_point_deform, handle);
+                                       handle[0] *= scalex;
+                                       handle[1] *= scaley;
+
+                                       cur_len = len_v2v2(co, handle);
+
+                                       if (cur_len < len) {
+                                               point_masklay = masklay;
+                                               point_spline = spline;
+                                               point = cur_point;
+                                               len = cur_len;
+                                               is_handle = TRUE;
+                                       }
+                               }
+
+                               cur_len = len_v2v2(co, vec);
+
+                               if (cur_len < len) {
+                                       point_spline = spline;
+                                       point_masklay = masklay;
+                                       point = cur_point;
+                                       len = cur_len;
+                                       is_handle = FALSE;
+                               }
+                       }
+               }
+       }
+
+       if (len < threshold) {
+               if (masklay_r)
+                       *masklay_r = point_masklay;
+
+               if (spline_r)
+                       *spline_r = point_spline;
+
+               if (is_handle_r)
+                       *is_handle_r = is_handle;
+
+               if (score)
+                       *score = len;
+
+               return point;
+       }
+
+       if (masklay_r)
+               *masklay_r = NULL;
+
+       if (spline_r)
+               *spline_r = NULL;
+
+       if (is_handle_r)
+               *is_handle_r = FALSE;
+
+       return NULL;
+}
+
+int ED_mask_feather_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold,
+                                 MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r,
+                                 MaskSplinePointUW **uw_r, float *score)
+{
+       MaskLayer *masklay, *point_masklay = NULL;
+       MaskSpline *point_spline = NULL;
+       MaskSplinePoint *point = NULL;
+       MaskSplinePointUW *uw = NULL;
+       float len = FLT_MAX, co[2];
+       float scalex, scaley, aspx, aspy;
+       int width, height;
+
+       ED_mask_size(C, &width, &height);
+       ED_mask_aspect(C, &aspx, &aspy);
+       ED_mask_pixelspace_factor(C, &scalex, &scaley);
+
+       co[0] = normal_co[0] * scalex;
+       co[1] = normal_co[1] * scaley;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
+
+                       int i, tot_feather_point;
+                       float (*feather_points)[2], (*fp)[2];
+
+                       if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                               continue;
+                       }
+
+                       feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
+
+                       for (i = 0; i < spline->tot_point; i++) {
+                               int j;
+                               MaskSplinePoint *cur_point = &spline->points[i];
+
+                               for (j = 0; j < cur_point->tot_uw + 1; j++) {
+                                       float cur_len, vec[2];
+
+                                       vec[0] = (*fp)[0] * scalex;
+                                       vec[1] = (*fp)[1] * scaley;
+
+                                       cur_len = len_v2v2(vec, co);
+
+                                       if (point == NULL || cur_len < len) {
+                                               if (j == 0)
+                                                       uw = NULL;
+                                               else
+                                                       uw = &cur_point->uw[j - 1];
+
+                                               point_masklay = masklay;
+                                               point_spline = spline;
+                                               point = cur_point;
+                                               len = cur_len;
+                                       }
+
+                                       fp++;
+                               }
+                       }
+
+                       MEM_freeN(feather_points);
+               }
+       }
+
+       if (len < threshold) {
+               if (masklay_r)
+                       *masklay_r = point_masklay;
+
+               if (spline_r)
+                       *spline_r = point_spline;
+
+               if (point_r)
+                       *point_r = point;
+
+               if (uw_r)
+                       *uw_r = uw;
+
+               if (score)
+                       *score = len;
+
+               return TRUE;
+       }
+
+       if (masklay_r)
+               *masklay_r = NULL;
+
+       if (spline_r)
+               *spline_r = NULL;
+
+       if (point_r)
+               *point_r = NULL;
+
+       return FALSE;
+}
+
+
+/******************** create new mask *********************/
+
+static int mask_new_exec(bContext *C, wmOperator *op)
+{
+       SpaceClip *sc = CTX_wm_space_clip(C);
+       Mask *mask;
+       char name[MAX_ID_NAME - 2];
+
+       RNA_string_get(op->ptr, "name", name);
+
+       mask = BKE_mask_new(name);
+
+       if (sc)
+               ED_space_clip_set_mask(C, sc, mask);
+
+       return OPERATOR_FINISHED;
+}
+
+void MASK_OT_new(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "New Mask";
+       ot->description = "Create new mask";
+       ot->idname = "MASK_OT_new";
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* api callbacks */
+       ot->exec = mask_new_exec;
+       ot->poll = ED_operator_mask;
+
+       /* properties */
+       RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask");
+}
+
+/******************** create new masklay *********************/
+
+static int masklay_new_exec(bContext *C, wmOperator *op)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       char name[MAX_ID_NAME - 2];
+
+       RNA_string_get(op->ptr, "name", name);
+
+       BKE_mask_layer_new(mask, name);
+       mask->masklay_act = mask->masklay_tot - 1;
+
+       WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+
+       return OPERATOR_FINISHED;
+}
+
+void MASK_OT_layer_new(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Add Mask Layer";
+       ot->description = "Add new mask layer for masking";
+       ot->idname = "MASK_OT_layer_new";
+
+       /* api callbacks */
+       ot->exec = masklay_new_exec;
+       ot->poll = ED_maskediting_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* properties */
+       RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask layer");
+}
+
+/******************** remove mask layer *********************/
+
+static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay = BKE_mask_layer_active(mask);
+
+       if (masklay) {
+               BKE_mask_layer_remove(mask, masklay);
+
+               WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+       }
+
+       return OPERATOR_FINISHED;
+}
+
+void MASK_OT_layer_remove(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Remove Mask Layer";
+       ot->description = "Remove mask layer";
+       ot->idname = "MASK_OT_layer_remove";
+
+       /* api callbacks */
+       ot->exec = masklay_remove_exec;
+       ot->poll = ED_maskediting_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/******************** slide *********************/
+
+#define SLIDE_ACTION_NONE       0
+#define SLIDE_ACTION_POINT      1
+#define SLIDE_ACTION_HANDLE     2
+#define SLIDE_ACTION_FEATHER    3
+
+typedef struct SlidePointData {
+       int action;
+
+       float co[2];
+       float vec[3][3];
+
+       Mask *mask;
+       MaskLayer *masklay;
+       MaskSpline *spline, *orig_spline;
+       MaskSplinePoint *point;
+       MaskSplinePointUW *uw;
+       float handle[2], no[2], feather[2];
+       int width, height;
+       float weight;
+
+       short curvature_only, accurate;
+       short initial_feather, overall_feather;
+} SlidePointData;
+
+static int slide_point_check_initial_feather(MaskSpline *spline)
+{
+       int i;
+
+       for (i = 0; i < spline->tot_point; i++) {
+               MaskSplinePoint *point = &spline->points[i];
+               int j;
+
+               if (point->bezt.weight != 0.0f)
+                       return FALSE;
+
+               for (j = 0; j < point->tot_uw; j++) {
+                       if (point->uw[j].w != 0.0f)
+                               return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+static void *slide_point_customdata(bContext *C, wmOperator *op, wmEvent *event)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       SlidePointData *customdata = NULL;
+       MaskLayer *masklay, *cv_masklay, *feather_masklay;
+       MaskSpline *spline, *cv_spline, *feather_spline;
+       MaskSplinePoint *point, *cv_point, *feather_point;
+       MaskSplinePointUW *uw = NULL;
+       int is_handle = FALSE, width, height, action = SLIDE_ACTION_NONE;
+       int slide_feather = RNA_boolean_get(op->ptr, "slide_feather");
+       float co[2], cv_score, feather_score;
+       const float threshold = 19;
+
+       ED_mask_mouse_pos(C, event, co);
+       ED_mask_size(C, &width, &height);
+
+       cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &is_handle, &cv_score);
+
+       if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) {
+               if (slide_feather || !cv_point || feather_score < cv_score) {
+                       action = SLIDE_ACTION_FEATHER;
+
+                       masklay = feather_masklay;
+                       spline = feather_spline;
+                       point = feather_point;
+               }
+       }
+
+       if (cv_point && action == SLIDE_ACTION_NONE) {
+               if (is_handle)
+                       action = SLIDE_ACTION_HANDLE;
+               else
+                       action = SLIDE_ACTION_POINT;
+
+               masklay = cv_masklay;
+               spline = cv_spline;
+               point = cv_point;
+       }
+
+       if (action != SLIDE_ACTION_NONE) {
+               customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data");
+
+               customdata->mask = mask;
+               customdata->masklay = masklay;
+               customdata->spline = spline;
+               customdata->point = point;
+               customdata->width = width;
+               customdata->height = height;
+               customdata->action = action;
+               customdata->uw = uw;
+
+               if (uw) {
+                       float co[2];
+
+                       customdata->weight = point->bezt.weight;
+
+                       customdata->weight = uw->w;
+                       BKE_mask_point_segment_co(spline, point, uw->u, co);
+                       BKE_mask_point_normal(spline, point, uw->u, customdata->no);
+
+                       customdata->feather[0] = co[0] + customdata->no[0] * uw->w;
+                       customdata->feather[1] = co[1] + customdata->no[1] * uw->w;
+               }
+               else {
+                       BezTriple *bezt = &point->bezt;
+
+                       BKE_mask_point_normal(spline, point, 0.0f, customdata->no);
+
+                       customdata->feather[0] = bezt->vec[1][0] + customdata->no[0] * bezt->weight;
+                       customdata->feather[1] = bezt->vec[1][1] + customdata->no[1] * bezt->weight;
+
+                       customdata->weight = bezt->weight;
+               }
+
+               if (customdata->action == SLIDE_ACTION_FEATHER)
+                       customdata->initial_feather = slide_point_check_initial_feather(spline);
+
+               copy_m3_m3(customdata->vec, point->bezt.vec);
+               if (BKE_mask_point_has_handle(point))
+                       BKE_mask_point_handle(point, customdata->handle);
+               ED_mask_mouse_pos(C, event, customdata->co);
+       }
+
+       return customdata;
+}
+
+static int slide_point_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       SlidePointData *slidedata = slide_point_customdata(C, op, event);
+
+       if (slidedata) {
+               Mask *mask = CTX_data_edit_mask(C);
+
+               op->customdata = slidedata;
+
+               WM_event_add_modal_handler(C, op);
+
+               if (slidedata->uw) {
+                       if ((slidedata->uw->flag & SELECT) == 0) {
+                               ED_mask_select_toggle_all(mask, SEL_DESELECT);
+
+                               slidedata->uw->flag |= SELECT;
+
+                               ED_mask_select_flush_all(mask);
+                       }
+               }
+               else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) {
+                       ED_mask_select_toggle_all(mask, SEL_DESELECT);
+
+                       BKE_mask_point_select_set(slidedata->point, TRUE);
+
+                       ED_mask_select_flush_all(mask);
+               }
+
+               slidedata->masklay->act_spline = slidedata->spline;
+               slidedata->masklay->act_point = slidedata->point;
+
+               WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+
+               return OPERATOR_RUNNING_MODAL;
+       }
+
+       return OPERATOR_PASS_THROUGH;
+}
+
+static void slide_point_delta_all_feather(SlidePointData *data, float delta)
+{
+       int i;
+
+       for (i = 0; i < data->spline->tot_point; i++) {
+               MaskSplinePoint *point = &data->spline->points[i];
+               MaskSplinePoint *orig_point = &data->orig_spline->points[i];
+               int j;
+
+               point->bezt.weight = orig_point->bezt.weight + delta;
+               if (point->bezt.weight < 0.0f)
+                       point->bezt.weight = 0.0f;
+
+               for (j = 0; j < point->tot_uw; j++) {
+                       point->uw[j].w = orig_point->uw[j].w + delta;
+                       if (point->uw[j].w < 0.0f)
+                               point->uw[j].w = 0.0f;
+               }
+       }
+}
+
+static void slide_point_restore_spline(SlidePointData *data)
+{
+       int i;
+
+       for (i = 0; i < data->spline->tot_point; i++) {
+               MaskSplinePoint *point = &data->spline->points[i];
+               MaskSplinePoint *orig_point = &data->orig_spline->points[i];
+               int j;
+
+               point->bezt = orig_point->bezt;
+
+               for (j = 0; j < point->tot_uw; j++)
+                       point->uw[j] = orig_point->uw[j];
+       }
+}
+
+static void cancel_slide_point(SlidePointData *data)
+{
+       /* cancel sliding */
+
+       if (data->orig_spline) {
+               slide_point_restore_spline(data);
+       }
+       else {
+               if (data->action == SLIDE_ACTION_FEATHER) {
+                       if (data->uw)
+                               data->uw->w = data->weight;
+                       else
+                               data->point->bezt.weight = data->weight;
+               }
+               else {
+                       copy_m3_m3(data->point->bezt.vec, data->vec);
+               }
+       }
+}
+
+static void free_slide_point_data(SlidePointData *data)
+{
+       if (data->orig_spline)
+               BKE_mask_spline_free(data->orig_spline);
+
+       MEM_freeN(data);
+}
+
+static int slide_point_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
+       SlidePointData *data = (SlidePointData *)op->customdata;
+       BezTriple *bezt = &data->point->bezt;
+       float co[2], dco[2];
+
+       switch (event->type) {
+               case LEFTCTRLKEY:
+               case RIGHTCTRLKEY:
+               case LEFTSHIFTKEY:
+               case RIGHTSHIFTKEY:
+                       if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY)) {
+                               if (data->action == SLIDE_ACTION_FEATHER)
+                                       data->overall_feather = event->val == KM_PRESS;
+                               else
+                                       data->curvature_only = event->val == KM_PRESS;
+                       }
+
+                       if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY))
+                               data->accurate = event->val == KM_PRESS;
+
+               /* no break! update CV position */
+
+               case MOUSEMOVE:
+                       ED_mask_mouse_pos(C, event, co);
+                       sub_v2_v2v2(dco, co, data->co);
+
+                       if (data->action == SLIDE_ACTION_HANDLE) {
+                               float delta[2], offco[2];
+
+                               sub_v2_v2v2(delta, data->handle, data->co);
+
+                               sub_v2_v2v2(offco, co, data->co);
+                               if (data->accurate)
+                                       mul_v2_fl(offco, 0.2f);
+                               add_v2_v2(offco, data->co);
+                               add_v2_v2(offco, delta);
+
+                               BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->handle, data->vec);
+                       }
+                       else if (data->action == SLIDE_ACTION_POINT) {
+                               float delta[2];
+
+                               copy_v2_v2(delta, dco);
+                               if (data->accurate)
+                                       mul_v2_fl(delta, 0.2f);
+
+                               add_v2_v2v2(bezt->vec[0], data->vec[0], delta);
+                               add_v2_v2v2(bezt->vec[1], data->vec[1], delta);
+                               add_v2_v2v2(bezt->vec[2], data->vec[2], delta);
+                       }
+                       else if (data->action == SLIDE_ACTION_FEATHER) {
+                               float vec[2], no[2], p[2], c[2], w, offco[2];
+                               float *weight = NULL;
+                               int overall_feather = data->overall_feather || data->initial_feather;
+
+                               add_v2_v2v2(offco, data->feather, dco);
+
+                               if (data->uw) {
+                                       float u = BKE_mask_spline_project_co(data->spline, data->point, data->uw->u, offco);
+
+                                       if (u > 0.0f && u < 1.0f) {
+                                               data->uw->u = u;
+
+                                               data->uw = BKE_mask_point_sort_uw(data->point, data->uw);
+                                               weight = &data->uw->w;
+                                               BKE_mask_point_normal(data->spline, data->point, data->uw->u, no);
+                                               BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p);
+                                       }
+                               }
+                               else {
+                                       weight = &bezt->weight;
+                                       copy_v2_v2(no, data->no);
+                                       copy_v2_v2(p, bezt->vec[1]);
+                               }
+
+                               if (weight) {
+                                       sub_v2_v2v2(c, offco, p);
+                                       project_v2_v2v2(vec, c, no);
+
+                                       w = len_v2(vec);
+
+                                       if (overall_feather) {
+                                               float delta;
+
+                                               if (dot_v2v2(no, vec) <= 0.0f)
+                                                       w = -w;
+
+                                               delta = w - data->weight;
+
+                                               if (data->orig_spline == NULL) {
+                                                       /* restore weight for currently sliding point, so orig_spline would be created
+                                                        * with original weights used
+                                                        */
+                                                       *weight = data->weight;
+
+                                                       data->orig_spline = BKE_mask_spline_copy(data->spline);
+                                               }
+
+                                               slide_point_delta_all_feather(data, delta);
+                                       }
+                                       else {
+                                               if (dot_v2v2(no, vec) <= 0.0f)
+                                                       w = 0.0f;
+
+                                               if (data->orig_spline) {
+                                                       /* restore possible overall feather changes */
+                                                       slide_point_restore_spline(data);
+
+                                                       BKE_mask_spline_free(data->orig_spline);
+                                                       data->orig_spline = NULL;
+                                               }
+
+                                               *weight = w;
+                                       }
+                               }
+                       }
+
+                       WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
+                       DAG_id_tag_update(&data->mask->id, 0);
+
+                       break;
+
+               case LEFTMOUSE:
+                       if (event->val == KM_RELEASE) {
+                               Scene *scene = CTX_data_scene(C);
+
+                               free_slide_point_data(op->customdata);
+
+                               if (IS_AUTOKEY_ON(scene)) {
+                                       ED_mask_layer_shape_auto_key_all(data->mask, CFRA);
+                               }
+
+                               WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
+                               DAG_id_tag_update(&data->mask->id, 0);
+
+                               return OPERATOR_FINISHED;
+                       }
+
+                       break;
+
+               case ESCKEY:
+                       cancel_slide_point(op->customdata);
+
+                       free_slide_point_data(op->customdata);
+
+                       WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask);
+                       DAG_id_tag_update(&data->mask->id, 0);
+
+                       return OPERATOR_CANCELLED;
+       }
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+void MASK_OT_slide_point(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Slide Point";
+       ot->description = "Slide control points";
+       ot->idname = "MASK_OT_slide_point";
+
+       /* api callbacks */
+       ot->invoke = slide_point_invoke;
+       ot->modal = slide_point_modal;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide slide feather instead of vertex");
+}
+
+/******************** toggle cyclic *********************/
+
+static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       if (ED_mask_spline_select_check(spline)) {
+                               spline->flag ^= MASK_SPLINE_CYCLIC;
+                       }
+               }
+       }
+
+       WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+
+       return OPERATOR_FINISHED;
+}
+
+void MASK_OT_cyclic_toggle(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Toggle Cyclic";
+       ot->description = "Toggle cyclic for selected splines";
+       ot->idname = "MASK_OT_cyclic_toggle";
+
+       /* api callbacks */
+       ot->exec = cyclic_toggle_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/******************** delete *********************/
+
+static void delete_feather_points(MaskSplinePoint *point)
+{
+       int i, count = 0;
+
+       if (!point->tot_uw)
+               return;
+
+       for (i = 0; i < point->tot_uw; i++) {
+               if ((point->uw[i].flag & SELECT) == 0)
+                       count++;
+       }
+
+       if (count == 0) {
+               MEM_freeN(point->uw);
+               point->uw = NULL;
+               point->tot_uw = 0;
+       }
+       else {
+               MaskSplinePointUW *new_uw;
+               int j = 0;
+
+               new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points");
+
+               for (i = 0; i < point->tot_uw; i++) {
+                       if ((point->uw[i].flag & SELECT) == 0) {
+                               new_uw[j++] = point->uw[i];
+                       }
+               }
+
+               MEM_freeN(point->uw);
+
+               point->uw = new_uw;
+               point->tot_uw = count;
+       }
+}
+
+static int delete_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       int mask_layer_shape_ofs = 0;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               spline = masklay->splines.first;
+
+               while (spline) {
+                       const int tot_point_orig = spline->tot_point;
+                       int i, count = 0;
+                       MaskSpline *next_spline = spline->next;
+
+                       /* count unselected points */
+                       for (i = 0; i < spline->tot_point; i++) {
+                               MaskSplinePoint *point = &spline->points[i];
+
+                               if (!MASKPOINT_ISSEL_ANY(point))
+                                       count++;
+                       }
+
+                       if (count == 0) {
+
+                               /* delete the whole spline */
+                               BLI_remlink(&masklay->splines, spline);
+                               BKE_mask_spline_free(spline);
+
+                               if (spline == masklay->act_spline) {
+                                       masklay->act_spline = NULL;
+                                       masklay->act_point = NULL;
+                               }
+
+                               BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig);
+                       }
+                       else {
+                               MaskSplinePoint *new_points;
+                               int j;
+
+                               new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints");
+
+                               for (i = 0, j = 0; i < tot_point_orig; i++) {
+                                       MaskSplinePoint *point = &spline->points[i];
+
+                                       if (!MASKPOINT_ISSEL_ANY(point)) {
+                                               if (point == masklay->act_point)
+                                                       masklay->act_point = &new_points[j];
+
+                                               delete_feather_points(point);
+
+                                               new_points[j] = *point;
+                                               j++;
+                                       }
+                                       else {
+                                               if (point == masklay->act_point)
+                                                       masklay->act_point = NULL;
+
+                                               BKE_mask_point_free(point);
+                                               spline->tot_point--;
+
+                                               BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1);
+                                       }
+                               }
+
+                               mask_layer_shape_ofs += spline->tot_point;
+
+                               MEM_freeN(spline->points);
+                               spline->points = new_points;
+
+                               ED_mask_select_flush_all(mask);
+                       }
+
+                       spline = next_spline;
+               }
+       }
+
+       /* TODO: only update edited splines */
+       BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
+
+       WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
+
+       return OPERATOR_FINISHED;
+}
+
+void MASK_OT_delete(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Delete";
+       ot->description = "Delete selected control points or splines";
+       ot->idname = "MASK_OT_delete";
+
+       /* api callbacks */
+       ot->invoke = WM_operator_confirm;
+       ot->exec = delete_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* *** switch direction *** */
+static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+
+       int change = FALSE;
+
+       /* do actual selection */
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       if (ED_mask_spline_select_check(spline)) {
+                               BKE_mask_spline_direction_switch(masklay, spline);
+                               change = TRUE;
+                       }
+               }
+       }
+
+       if (change) {
+               /* TODO: only update this spline */
+               BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra);
+
+               WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+
+               return OPERATOR_FINISHED;
+       }
+
+       return OPERATOR_CANCELLED;
+}
+
+void MASK_OT_switch_direction(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Switch Direction";
+       ot->description = "Switch direction of selected splines";
+       ot->idname = "MASK_OT_switch_direction";
+
+       /* api callbacks */
+       ot->exec = mask_switch_direction_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+
+/******************** set handle type *********************/
+
+static int set_handle_type_exec(bContext *C, wmOperator *op)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       int handle_type = RNA_enum_get(op->ptr, "type");
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+               int i;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       for (i = 0; i < spline->tot_point; i++) {
+                               MaskSplinePoint *point = &spline->points[i];
+
+                               if (MASKPOINT_ISSEL_ANY(point)) {
+                                       BezTriple *bezt = &point->bezt;
+
+                                       bezt->h1 = bezt->h2 = handle_type;
+                               }
+                       }
+               }
+       }
+
+       WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
+       DAG_id_tag_update(&mask->id, 0);
+
+       return OPERATOR_FINISHED;
+}
+
+void MASK_OT_handle_type_set(wmOperatorType *ot)
+{
+       static EnumPropertyItem editcurve_handle_type_items[] = {
+               {HD_AUTO, "AUTO", 0, "Auto", ""},
+               {HD_VECT, "VECTOR", 0, "Vector", ""},
+               {HD_ALIGN, "ALIGNED", 0, "Aligned", ""},
+               {0, NULL, 0, NULL, NULL}
+       };
+
+       /* identifiers */
+       ot->name = "Set Handle Type";
+       ot->description = "Set type of handles for selected control points";
+       ot->idname = "MASK_OT_handle_type_set";
+
+       /* api callbacks */
+       ot->invoke = WM_menu_invoke;
+       ot->exec = set_handle_type_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* properties */
+       ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
+}
+
+
+/* ********* clear/set restrict view *********/
+static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       int changed = FALSE;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+
+               if (masklay->restrictflag & OB_RESTRICT_VIEW) {
+                       ED_mask_layer_select_set(masklay, TRUE);
+                       masklay->restrictflag &= ~OB_RESTRICT_VIEW;
+                       changed = 1;
+               }
+       }
+
+       if (changed) {
+               WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
+               DAG_id_tag_update(&mask->id, 0);
+
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+}
+
+void MASK_OT_hide_view_clear(wmOperatorType *ot)
+{
+
+       /* identifiers */
+       ot->name = "Clear Restrict View";
+       ot->description = "Reveal the layer by setting the hide flag";
+       ot->idname = "MASK_OT_hide_view_clear";
+
+       /* api callbacks */
+       ot->exec = mask_hide_view_clear_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       const int unselected = RNA_boolean_get(op->ptr, "unselected");
+       int changed = FALSE;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+
+               if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
+                       continue;
+               }
+
+               if (!unselected) {
+                       if (ED_mask_layer_select_check(masklay)) {
+                               ED_mask_layer_select_set(masklay, FALSE);
+
+                               masklay->restrictflag |= OB_RESTRICT_VIEW;
+                               changed = 1;
+                               if (masklay == BKE_mask_layer_active(mask)) {
+                                       BKE_mask_layer_active_set(mask, NULL);
+                               }
+                       }
+               }
+               else {
+                       if (!ED_mask_layer_select_check(masklay)) {
+                               masklay->restrictflag |= OB_RESTRICT_VIEW;
+                               changed = 1;
+                               if (masklay == BKE_mask_layer_active(mask)) {
+                                       BKE_mask_layer_active_set(mask, NULL);
+                               }
+                       }
+               }
+       }
+
+       if (changed) {
+               WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask);
+               DAG_id_tag_update(&mask->id, 0);
+
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+}
+
+void MASK_OT_hide_view_set(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Set Restrict View";
+       ot->description = "Hide the layer by setting the hide flag";
+       ot->idname = "MASK_OT_hide_view_set";
+
+       /* api callbacks */
+       ot->exec = mask_hide_view_set_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers");
+
+}
diff --git a/source/blender/editors/mask/mask_relationships.c b/source/blender/editors/mask/mask_relationships.c
new file mode 100644 (file)
index 0000000..8a8427c
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ *                 Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_relationshops.c
+ *  \ingroup edmask
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_mask.h"
+#include "BKE_tracking.h"
+
+#include "DNA_mask_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"  /* SELECT */
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_mask.h"
+#include "ED_clip.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "mask_intern.h"  /* own include */
+
+static int mask_parent_clear_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+               int i;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       for (i = 0; i < spline->tot_point; i++) {
+                               MaskSplinePoint *point = &spline->points[i];
+
+                               if (MASKPOINT_ISSEL_ANY(point)) {
+                                       point->parent.flag &= ~MASK_PARENT_ACTIVE;
+                               }
+                       }
+               }
+       }
+
+       WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
+       DAG_id_tag_update(&mask->id, 0);
+
+       return OPERATOR_FINISHED;
+}
+
+void MASK_OT_parent_clear(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Clear Parent";
+       ot->description = "Clear the masks parenting";
+       ot->idname = "MASK_OT_parent_clear";
+
+       /* api callbacks */
+       ot->exec = mask_parent_clear_exec;
+
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int mask_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+
+       /* parent info */
+       SpaceClip *sc;
+       MovieClip *clip;
+       MovieTrackingTrack *track;
+       MovieTrackingMarker *marker;
+       MovieTrackingObject *tracking;
+       /* done */
+
+       float marker_pos_ofs[2];
+       float parmask_pos[2];
+
+       if ((NULL == (sc = CTX_wm_space_clip(C))) ||
+           (NULL == (clip = sc->clip)) ||
+           (NULL == (track = clip->tracking.act_track)) ||
+           (NULL == (marker = BKE_tracking_get_marker(track, sc->user.framenr))) ||
+           (NULL == (tracking = BKE_tracking_active_object(&clip->tracking))))
+       {
+               return OPERATOR_CANCELLED;
+       }
+
+       add_v2_v2v2(marker_pos_ofs, marker->pos, track->offset);
+
+       BKE_mask_coord_from_movieclip(clip, &sc->user, parmask_pos, marker_pos_ofs);
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+               int i;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       for (i = 0; i < spline->tot_point; i++) {
+                               MaskSplinePoint *point = &spline->points[i];
+
+                               if (MASKPOINT_ISSEL_ANY(point)) {
+                                       point->parent.id_type = ID_MC;
+                                       point->parent.id = &clip->id;
+                                       strcpy(point->parent.parent, tracking->name);
+                                       strcpy(point->parent.sub_parent, track->name);
+
+                                       point->parent.flag |= MASK_PARENT_ACTIVE;
+
+                                       copy_v2_v2(point->parent.parent_orig, parmask_pos);
+                               }
+                       }
+               }
+       }
+
+       WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
+       DAG_id_tag_update(&mask->id, 0);
+
+       return OPERATOR_FINISHED;
+}
+
+/** based on #OBJECT_OT_parent_set */
+void MASK_OT_parent_set(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Make Parent";
+       ot->description = "Set the masks parenting";
+       ot->idname = "MASK_OT_parent_set";
+
+       /* api callbacks */
+       //ot->invoke = mask_parent_set_invoke;
+       ot->exec = mask_parent_set_exec;
+
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c
new file mode 100644 (file)
index 0000000..18d745d
--- /dev/null
@@ -0,0 +1,770 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ *                 Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_select.c
+ *  \ingroup edmask
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
+#include "BLI_rect.h"
+#include "BLI_lasso.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_mask.h"
+
+#include "DNA_mask_types.h"
+#include "DNA_object_types.h"  /* SELECT */
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_mask.h"
+#include "ED_clip.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "mask_intern.h"  /* own include */
+
+/* 'check' select */
+int ED_mask_spline_select_check(MaskSpline *spline)
+{
+       int i;
+
+       for (i = 0; i < spline->tot_point; i++) {
+               MaskSplinePoint *point = &spline->points[i];
+
+               if (MASKPOINT_ISSEL_ANY(point))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+int ED_mask_layer_select_check(MaskLayer *masklay)
+{
+       MaskSpline *spline;
+
+       if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+               return FALSE;
+       }
+
+       for (spline = masklay->splines.first; spline; spline = spline->next) {
+               if (ED_mask_spline_select_check(spline)) {
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+int ED_mask_select_check(Mask *mask)
+{
+       MaskLayer *masklay;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               if (ED_mask_layer_select_check(masklay)) {
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+/* 'sel' select  */
+void ED_mask_spline_select_set(MaskSpline *spline, const short do_select)
+{
+       int i;
+
+       if (do_select)
+               spline->flag |= SELECT;
+       else
+               spline->flag &= ~SELECT;
+
+       for (i = 0; i < spline->tot_point; i++) {
+               MaskSplinePoint *point = &spline->points[i];
+
+               BKE_mask_point_select_set(point, do_select);
+       }
+}
+
+void ED_mask_layer_select_set(MaskLayer *masklay, const short do_select)
+{
+       MaskSpline *spline;
+
+       if (masklay->restrictflag & MASK_RESTRICT_SELECT) {
+               if (do_select == TRUE) {
+                       return;
+               }
+       }
+
+       for (spline = masklay->splines.first; spline; spline = spline->next) {
+               ED_mask_spline_select_set(spline, do_select);
+       }
+}
+
+void ED_mask_select_toggle_all(Mask *mask, int action)
+{
+       MaskLayer *masklay;
+
+       if (action == SEL_TOGGLE) {
+               if (ED_mask_select_check(mask))
+                       action = SEL_DESELECT;
+               else
+                       action = SEL_SELECT;
+       }
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+
+               if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
+                       continue;
+               }
+
+               ED_mask_layer_select_set(masklay, (action == SEL_SELECT) ? TRUE : FALSE);
+       }
+}
+
+void ED_mask_select_flush_all(Mask *mask)
+{
+       MaskLayer *masklay;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       int i;
+
+                       spline->flag &= ~SELECT;
+
+                       /* intentionally _dont_ do this in the masklay loop
+                        * so we clear flags on all splines */
+                       if (masklay->restrictflag & MASK_RESTRICT_VIEW) {
+                               continue;
+                       }
+
+                       for (i = 0; i < spline->tot_point; i++) {
+                               MaskSplinePoint *cur_point = &spline->points[i];
+
+                               if (MASKPOINT_ISSEL_ANY(cur_point)) {
+                                       spline->flag |= SELECT;
+                               }
+                               else {
+                                       int j;
+
+                                       for (j = 0; j < cur_point->tot_uw; j++) {
+                                               if (cur_point->uw[j].flag & SELECT) {
+                                                       spline->flag |= SELECT;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+/******************** toggle selection *********************/
+
+static int select_all_exec(bContext *C, wmOperator *op)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       int action = RNA_enum_get(op->ptr, "action");
+
+       ED_mask_select_toggle_all(mask, action);
+       ED_mask_select_flush_all(mask);
+
+       WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+
+       return OPERATOR_FINISHED;
+}
+
+void MASK_OT_select_all(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Select or Deselect All";
+       ot->description = "Change selection of all curve points";
+       ot->idname = "MASK_OT_select_all";
+
+       /* api callbacks */
+       ot->exec = select_all_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* properties */
+       WM_operator_properties_select_all(ot);
+}
+
+/******************** select *********************/
+
+static int select_exec(bContext *C, wmOperator *op)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       MaskSpline *spline;
+       MaskSplinePoint *point = NULL;
+       float co[2];
+       short extend = RNA_boolean_get(op->ptr, "extend");
+       short deselect = RNA_boolean_get(op->ptr, "deselect");
+       short toggle = RNA_boolean_get(op->ptr, "toggle");
+
+       int is_handle = 0;
+       const float threshold = 19;
+
+       RNA_float_get_array(op->ptr, "location", co);
+
+       point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &is_handle, NULL);
+
+       if (extend == 0 && deselect == 0 && toggle == 0)
+               ED_mask_select_toggle_all(mask, SEL_DESELECT);
+
+       if (point) {
+
+               if (is_handle) {
+                       if (extend) {
+                               masklay->act_spline = spline;
+                               masklay->act_point = point;
+
+                               BKE_mask_point_select_set_handle(point, TRUE);
+                       }
+                       else if (deselect) {
+                               BKE_mask_point_select_set_handle(point, FALSE);
+                       }
+                       else {
+                               masklay->act_spline = spline;
+                               masklay->act_point = point;
+
+                               if (!MASKPOINT_ISSEL_HANDLE(point)) {
+                                       BKE_mask_point_select_set_handle(point, TRUE);
+                               }
+                               else if (toggle) {
+                                       BKE_mask_point_select_set_handle(point, FALSE);
+                               }
+                       }
+               }
+               else {
+                       if (extend) {
+                               masklay->act_spline = spline;
+                               masklay->act_point = point;
+
+                               BKE_mask_point_select_set(point, TRUE);
+                       }
+                       else if (deselect) {
+                               BKE_mask_point_select_set(point, FALSE);
+                       }
+                       else {
+                               masklay->act_spline = spline;
+                               masklay->act_point = point;
+
+                               if (!MASKPOINT_ISSEL_ANY(point)) {
+                                       BKE_mask_point_select_set(point, TRUE);
+                               }
+                               else if (toggle) {
+                                       BKE_mask_point_select_set(point, FALSE);
+                               }
+                       }
+               }
+
+               masklay->act_spline = spline;
+               masklay->act_point = point;
+
+               ED_mask_select_flush_all(mask);
+
+               WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+
+               return OPERATOR_FINISHED;
+       }
+       else {
+               MaskSplinePointUW *uw;
+
+               if (ED_mask_feather_find_nearest(C, mask, co, threshold, &masklay, &spline, &point, &uw, NULL)) {
+
+                       if (extend) {
+                               masklay->act_spline = spline;
+                               masklay->act_point = point;
+
+                               if (uw) uw->flag |= SELECT;
+                       }
+                       else if (deselect) {
+                               if (uw) uw->flag &= ~SELECT;
+                       }
+                       else {
+                               masklay->act_spline = spline;
+                               masklay->act_point = point;
+
+                               if (uw) {
+                                       if (!(uw->flag & SELECT)) {
+                                               uw->flag |= SELECT;
+                                       }
+                                       else if (toggle) {
+                                               uw->flag &= ~SELECT;
+                                       }
+                               }
+                       }
+
+                       ED_mask_select_flush_all(mask);
+
+                       WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+
+                       return OPERATOR_FINISHED;
+               }
+       }
+
+       return OPERATOR_PASS_THROUGH;
+}
+
+static int select_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       float co[2];
+
+       ED_mask_mouse_pos(C, event, co);
+
+       RNA_float_set_array(op->ptr, "location", co);
+
+       return select_exec(C, op);
+}
+
+void MASK_OT_select(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Select";
+       ot->description = "Select spline points";
+       ot->idname = "MASK_OT_select";
+
+       /* api callbacks */
+       ot->exec = select_exec;
+       ot->invoke = select_invoke;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
+
+       /* properties */
+       WM_operator_properties_mouse_select(ot);
+
+       RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX,
+                            "Location", "Location of vertex in normalized space", -1.0f, 1.0f);
+}
+
+
+
+/********************** border select operator *********************/
+
+static int border_select_exec(bContext *C, wmOperator *op)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       int i;
+
+       rcti rect;
+       rctf rectf;
+       int change = FALSE, mode, extend;
+
+       /* get rectangle from operator */
+       rect.xmin = RNA_int_get(op->ptr, "xmin");
+       rect.ymin = RNA_int_get(op->ptr, "ymin");
+       rect.xmax = RNA_int_get(op->ptr, "xmax");
+       rect.ymax = RNA_int_get(op->ptr, "ymax");
+
+       ED_mask_point_pos(C, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin);
+       ED_mask_point_pos(C, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
+
+       mode = RNA_int_get(op->ptr, "gesture_mode");
+       extend = RNA_boolean_get(op->ptr, "extend");
+
+       /* do actual selection */
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
+
+                       for (i = 0; i < spline->tot_point; i++) {
+                               MaskSplinePoint *point = &spline->points[i];
+                               MaskSplinePoint *point_deform = &points_array[i];
+
+                               /* TODO: handles? */
+                               /* TODO: uw? */
+
+                               if (BLI_in_rctf(&rectf, point_deform->bezt.vec[1][0], point_deform->bezt.vec[1][1])) {
+                                       BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
+                                       BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT);
+                               }
+                               else if (!extend) {
+                                       BKE_mask_point_select_set(point, FALSE);
+                                       BKE_mask_point_select_set_handle(point, FALSE);
+                               }
+
+                               change = TRUE;
+                       }
+               }
+       }
+
+       if (change) {
+               ED_mask_select_flush_all(mask);
+
+               WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+
+               return OPERATOR_FINISHED;
+       }
+
+       return OPERATOR_CANCELLED;
+}
+
+void MASK_OT_select_border(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Border Select";
+       ot->description = "Select markers using border selection";
+       ot->idname = "MASK_OT_select_border";
+
+       /* api callbacks */
+       ot->invoke = WM_border_select_invoke;
+       ot->exec = border_select_exec;
+       ot->modal = WM_border_select_modal;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
+
+       /* properties */
+       WM_operator_properties_gesture_border(ot, TRUE);
+}
+
+static int do_lasso_select_mask(bContext *C, int mcords[][2], short moves, short select)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       int i;
+
+       rcti rect;
+       int change = FALSE;
+
+       /* get rectangle from operator */
+       BLI_lasso_boundbox(&rect, mcords, moves);
+
+       /* do actual selection */
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
+
+                       for (i = 0; i < spline->tot_point; i++) {
+                               MaskSplinePoint *point = &spline->points[i];
+                               MaskSplinePoint *point_deform = &points_array[i];
+
+                               /* TODO: handles? */
+                               /* TODO: uw? */
+
+                               float screen_co[2];
+
+                               /* marker in screen coords */
+                               ED_mask_point_pos__reverse(C,
+                                                          point_deform->bezt.vec[1][0], point_deform->bezt.vec[1][1],
+                                                          &screen_co[0], &screen_co[1]);
+
+                               if (BLI_in_rcti(&rect, screen_co[0], screen_co[1]) &&
+                                   BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX))
+                               {
+                                       BKE_mask_point_select_set(point, select);
+                                       BKE_mask_point_select_set_handle(point, select);
+                               }
+
+                               change = TRUE;
+                       }
+               }
+       }
+
+       if (change) {
+               ED_mask_select_flush_all(mask);
+
+               WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+       }
+
+       return change;
+}
+
+static int clip_lasso_select_exec(bContext *C, wmOperator *op)
+{
+       int mcords_tot;
+       int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
+
+       if (mcords) {
+               short select;
+
+               select = !RNA_boolean_get(op->ptr, "deselect");
+               do_lasso_select_mask(C, mcords, mcords_tot, select);
+
+               MEM_freeN(mcords);
+
+               return OPERATOR_FINISHED;
+       }
+       return OPERATOR_PASS_THROUGH;
+}
+
+void MASK_OT_select_lasso(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Lasso Select";
+       ot->description = "Select markers using lasso selection";
+       ot->idname = "MASK_OT_select_lasso";
+
+       /* api callbacks */
+       ot->invoke = WM_gesture_lasso_invoke;
+       ot->modal = WM_gesture_lasso_modal;
+       ot->exec = clip_lasso_select_exec;
+       ot->poll = ED_maskediting_mask_poll;
+       ot->cancel = WM_gesture_lasso_cancel;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
+
+       /* properties */
+       RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
+       RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items");
+       RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
+}
+
+/********************** circle select operator *********************/
+
+static int mask_spline_point_inside_ellipse(BezTriple *bezt, float offset[2], float ellipse[2])
+{
+       /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
+       float x, y;
+
+       x = (bezt->vec[1][0] - offset[0]) * ellipse[0];
+       y = (bezt->vec[1][1] - offset[1]) * ellipse[1];
+
+       return x * x + y * y < 1.0f;
+}
+
+static int circle_select_exec(bContext *C, wmOperator *op)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       int i;
+
+       SpaceClip *sc = CTX_wm_space_clip(C);
+       ARegion *ar = CTX_wm_region(C);
+       int x, y, radius, width, height, mode, change = FALSE;
+       float zoomx, zoomy, offset[2], ellipse[2];
+
+       /* get operator properties */
+       x = RNA_int_get(op->ptr, "x");
+       y = RNA_int_get(op->ptr, "y");
+       radius = RNA_int_get(op->ptr, "radius");
+
+       mode = RNA_int_get(op->ptr, "gesture_mode");
+
+       /* TODO - make generic! - this is SpaceClip only! */
+       /* compute ellipse and position in unified coordinates */
+       ED_space_clip_size(sc, &width, &height);
+       ED_space_clip_zoom(sc, ar, &zoomx, &zoomy);
+       width = height = MAX2(width, height);
+
+       ellipse[0] = width * zoomx / radius;
+       ellipse[1] = height * zoomy / radius;
+
+       ED_mask_point_pos(C, x, y, &offset[0], &offset[1]);
+
+       /* do actual selection */
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
+
+                       for (i = 0; i < spline->tot_point; i++) {
+                               MaskSplinePoint *point = &spline->points[i];
+                               MaskSplinePoint *point_deform = &points_array[i];
+
+                               if (mask_spline_point_inside_ellipse(&point_deform->bezt, offset, ellipse)) {
+                                       BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT);
+                                       BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT);
+
+                                       change = TRUE;
+                               }
+                       }
+               }
+       }
+
+       if (change) {
+               ED_mask_select_flush_all(mask);
+
+               WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+
+               return OPERATOR_FINISHED;
+       }
+
+       return OPERATOR_CANCELLED;
+}
+
+void MASK_OT_select_circle(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Circle Select";
+       ot->description = "Select markers using circle selection";
+       ot->idname = "MASK_OT_select_circle";
+
+       /* api callbacks */
+       ot->invoke = WM_gesture_circle_invoke;
+       ot->modal = WM_gesture_circle_modal;
+       ot->exec = circle_select_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       /* properties */
+       RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
+       RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
+       RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
+       RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
+}
+
+static int mask_select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       MaskSpline *spline;
+       MaskSplinePoint *point = NULL;
+       float co[2];
+       int do_select = !RNA_boolean_get(op->ptr, "deselect");
+
+       int is_handle = 0;
+       const float threshold = 19;
+       int change = FALSE;
+
+       ED_mask_mouse_pos(C, event, co);
+
+       point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &is_handle, NULL);
+
+       if (point) {
+               ED_mask_spline_select_set(spline, do_select);
+               masklay->act_spline = spline;
+               masklay->act_point = point;
+
+               change = TRUE;
+       }
+
+       if (change) {
+               ED_mask_select_flush_all(mask);
+
+               WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+
+               return OPERATOR_FINISHED;
+       }
+
+       return OPERATOR_CANCELLED;
+}
+
+void MASK_OT_select_linked_pick(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Select Linked";
+       ot->idname = "MASK_OT_select_linked_pick";
+       ot->description = "(De)select all points linked to the curve under the mouse cursor";
+
+       /* api callbacks */
+       ot->invoke = mask_select_linked_pick_invoke;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
+}
+
+static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+
+       int change = FALSE;
+
+       /* do actual selection */
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskSpline *spline;
+
+               if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+                       continue;
+               }
+
+               for (spline = masklay->splines.first; spline; spline = spline->next) {
+                       if (ED_mask_spline_select_check(spline)) {
+                               ED_mask_spline_select_set(spline, TRUE);
+                               change = TRUE;
+                       }
+               }
+       }
+
+       if (change) {
+               ED_mask_select_flush_all(mask);
+
+               WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+
+               return OPERATOR_FINISHED;
+       }
+
+       return OPERATOR_CANCELLED;
+}
+
+void MASK_OT_select_linked(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Select Linked All";
+       ot->idname = "MASK_OT_select_linked";
+       ot->description = "Select all vertices linked to the active mesh";
+
+       /* api callbacks */
+       ot->exec = mask_select_linked_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
new file mode 100644 (file)
index 0000000..94aebb9
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2012 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation,
+ *                 Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/mask/mask_shapekey.c
+ *  \ingroup edmask
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_mask.h"
+
+#include "DNA_mask_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"  /* SELECT */
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_mask.h"
+#include "ED_clip.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "mask_intern.h"  /* own include */
+
+static int mask_shape_key_insert_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Scene *scene = CTX_data_scene(C);
+       const int frame = CFRA;
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       int change = FALSE;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskLayerShape *masklay_shape;
+
+               if (!ED_mask_layer_select_check(masklay)) {
+                       continue;
+               }
+
+               masklay_shape = BKE_mask_layer_shape_varify_frame(masklay, frame);
+               BKE_mask_layer_shape_from_mask(masklay, masklay_shape);
+               change = TRUE;
+       }
+
+       if (change) {
+               WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
+               DAG_id_tag_update(&mask->id, 0);
+
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+}
+
+void MASK_OT_shape_key_insert(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Insert Shape Key";
+       ot->description = "";
+       ot->idname = "MASK_OT_shape_key_insert";
+
+       /* api callbacks */
+       ot->exec = mask_shape_key_insert_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int mask_shape_key_clear_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Scene *scene = CTX_data_scene(C);
+       const int frame = CFRA;
+       Mask *mask = CTX_data_edit_mask(C);
+       MaskLayer *masklay;
+       int change = FALSE;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskLayerShape *masklay_shape;
+
+               if (!ED_mask_layer_select_check(masklay)) {
+                       continue;
+               }
+
+               masklay_shape = BKE_mask_layer_shape_find_frame(masklay, frame);
+
+               if (masklay_shape) {
+                       BKE_mask_layer_shape_unlink(masklay, masklay_shape);
+                       change = TRUE;
+               }
+       }
+
+       if (change) {
+               WM_event_add_notifier(C, NC_MASK | ND_DATA, mask);
+               DAG_id_tag_update(&mask->id, 0);
+
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
+}
+
+void MASK_OT_shape_key_clear(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Clear Shape Key";
+       ot->description = "";
+       ot->idname = "MASK_OT_shape_key_clear";
+
+       /* api callbacks */
+       ot->exec = mask_shape_key_clear_exec;
+       ot->poll = ED_maskediting_mask_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+int ED_mask_layer_shape_auto_key_all(Mask *mask, const int frame)
+{
+       MaskLayer *masklay;
+       int change = FALSE;
+
+       for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+               MaskLayerShape *masklay_shape;
+
+               masklay_shape = BKE_mask_layer_shape_varify_frame(masklay, frame);
+               BKE_mask_layer_shape_from_mask(masklay, masklay_shape);
+               change = TRUE;
+       }
+
+       return change;
+}