GSOC 2013 paint
[blender-staging.git] / source / blender / editors / sculpt_paint / paint_curve.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/sculpt_paint/paint_curve.c
22  *  \ingroup edsculpt
23  */
24
25 #include <string.h>
26 #include <limits.h>
27
28 #include "MEM_guardedalloc.h"
29
30 #include "DNA_brush_types.h"
31 #include "DNA_object_types.h"
32 #include "DNA_screen_types.h"
33 #include "DNA_space_types.h"
34 #include "DNA_view3d_types.h"
35
36 #include "BKE_context.h"
37 #include "BKE_main.h"
38 #include "BKE_paint.h"
39
40 #include "BLI_math_vector.h"
41 #include "BLI_string.h"
42
43 #include "ED_paint.h"
44 #include "ED_view3d.h"
45
46 #include "WM_api.h"
47 #include "WM_types.h"
48
49 #include "RNA_access.h"
50 #include "RNA_define.h"
51
52 #include "UI_view2d.h"
53
54 #include "paint_intern.h"
55
56 #define PAINT_CURVE_SELECT_THRESHOLD 40.0f
57 #define PAINT_CURVE_POINT_SELECT(pcp, i) (*(&pcp->bez.f1 + i) = SELECT)
58
59
60 int paint_curve_poll(bContext *C)
61 {
62         Object *ob = CTX_data_active_object(C);
63         Paint *p;
64         RegionView3D *rv3d = CTX_wm_region_view3d(C);
65         SpaceImage *sima;
66
67         if (rv3d && !(ob && ((ob->mode & OB_MODE_ALL_PAINT) != 0)))
68                 return false;
69
70         sima = CTX_wm_space_image(C);
71
72         if (sima && sima->mode != SI_MODE_PAINT)
73                 return false;
74
75         p = BKE_paint_get_active_from_context(C);
76
77         if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) {
78                 return true;
79         }
80
81         return false;
82 }
83
84 /* Paint Curve Undo*/
85
86 typedef struct UndoCurve {
87         struct UndoImageTile *next, *prev;
88
89         PaintCurvePoint *points; /* points of curve */
90         int tot_points;
91         int active_point;
92
93         char idname[MAX_ID_NAME];  /* name instead of pointer*/
94 } UndoCurve;
95
96 static void paintcurve_undo_restore(bContext *C, ListBase *lb)
97 {
98         Paint *p = BKE_paint_get_active_from_context(C);
99         UndoCurve *uc;
100         PaintCurve *pc;
101
102         if (p->brush) {
103                 pc = p->brush->paint_curve;
104         }
105
106         if (!pc)
107                 return;
108
109         uc = (UndoCurve *)lb->first;
110
111         if (strncmp(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname))) == 0) {
112                 SWAP(PaintCurvePoint *, pc->points, uc->points);
113                 SWAP(int, pc->tot_points, uc->tot_points);
114                 SWAP(int, pc->add_index, uc->active_point);
115         }
116 }
117
118 static void paintcurve_undo_delete(ListBase *lb)
119 {
120         UndoCurve *uc;
121         uc = (UndoCurve *)lb->first;
122
123         if (uc->points)
124                 MEM_freeN(uc->points);
125         uc->points = NULL;
126 }
127
128
129 static void paintcurve_undo_begin(bContext *C, wmOperator *op, PaintCurve *pc)
130 {
131         PaintMode mode = BKE_paintmode_get_active_from_context(C);
132         ListBase *lb = NULL;
133         int undo_stack_id;
134         UndoCurve *uc;
135
136         switch (mode) {
137                 case PAINT_TEXTURE_2D:
138                 case PAINT_TEXTURE_PROJECTIVE:
139                         undo_stack_id = UNDO_PAINT_IMAGE;
140                         break;
141
142                 case PAINT_SCULPT:
143                         undo_stack_id = UNDO_PAINT_MESH;
144                         break;
145
146                 default:
147                         /* do nothing, undo is handled by global */
148                         return;
149         }
150
151
152         ED_undo_paint_push_begin(undo_stack_id, op->type->name,
153                                  paintcurve_undo_restore, paintcurve_undo_delete, NULL);
154         lb = undo_paint_push_get_list(undo_stack_id);
155
156         uc = MEM_callocN(sizeof(*uc), "Undo_curve");
157
158         lb->first = uc;
159
160         BLI_strncpy(uc->idname, pc->id.name, sizeof(uc->idname));
161         uc->tot_points = pc->tot_points;
162         uc->active_point = pc->add_index;
163         uc->points = MEM_dupallocN(pc->points);
164
165         undo_paint_push_count_alloc(undo_stack_id, sizeof(*uc) + sizeof(*pc->points) * pc->tot_points);
166
167         ED_undo_paint_push_end(undo_stack_id);
168 }
169 #define SEL_F1 (1 << 0)
170 #define SEL_F2 (1 << 1)
171 #define SEL_F3 (1 << 2)
172
173 /* returns 0, 1, or 2 in point according to handle 1, pivot or handle 2 */
174 static PaintCurvePoint *paintcurve_point_get_closest(PaintCurve *pc, const float pos[2], bool ignore_pivot, const float threshold, char *point)
175 {
176         PaintCurvePoint *pcp, *closest = NULL;
177         int i;
178         float dist, closest_dist = FLT_MAX;
179
180         for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) {
181                 dist = len_manhattan_v2v2(pos, pcp->bez.vec[0]);
182                 if (dist < threshold) {
183                         if (dist < closest_dist) {
184                                 closest = pcp;
185                                 closest_dist = dist;
186                                 if (point)
187                                         *point = SEL_F1;
188                         }
189                 }
190                 if (!ignore_pivot) {
191                         dist = len_manhattan_v2v2(pos, pcp->bez.vec[1]);
192                         if (dist < threshold) {
193                                 if (dist < closest_dist) {
194                                         closest = pcp;
195                                         closest_dist = dist;
196                                         if (point)
197                                                 *point = SEL_F2;
198                                 }
199                         }
200                 }
201                 dist = len_manhattan_v2v2(pos, pcp->bez.vec[2]);
202                 if (dist < threshold) {
203                         if (dist < closest_dist) {
204                                 closest = pcp;
205                                 closest_dist = dist;
206                                 if (point)
207                                         *point = SEL_F3;
208                         }
209                 }
210         }
211
212         return closest;
213 }
214
215 static int paintcurve_point_co_index(char sel)
216 {
217         char i = 0;
218         while (sel != 1) {
219                 sel >>= 1;
220                 i++;
221         }
222         return i;
223 }
224
225 /******************* Operators *********************************/
226
227 static int paintcurve_new_exec(bContext *C, wmOperator *UNUSED(op))
228 {
229         Paint *p = BKE_paint_get_active_from_context(C);
230         Main *bmain = CTX_data_main(C);
231
232         if (p && p->brush) {
233                 p->brush->paint_curve = BKE_paint_curve_add(bmain, "PaintCurve");
234         }
235
236         return OPERATOR_FINISHED;
237 }
238
239 void PAINTCURVE_OT_new(wmOperatorType *ot)
240 {
241         /* identifiers */
242         ot->name = "Add New Paint Curve";
243         ot->description = "Add new paint curve";
244         ot->idname = "PAINTCURVE_OT_new";
245
246         /* api callbacks */
247         ot->exec = paintcurve_new_exec;
248         ot->poll = paint_curve_poll;
249
250         /* flags */
251         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
252 }
253
254 static void paintcurve_point_add(bContext *C,  wmOperator *op, const int loc[2])
255 {
256         Paint *p = BKE_paint_get_active_from_context(C);
257         Brush *br = p->brush;
258         Main *bmain = CTX_data_main(C);
259         PaintCurve *pc;
260         PaintCurvePoint *pcp;
261         wmWindow *window = CTX_wm_window(C);
262         ARegion *ar = CTX_wm_region(C);
263         float vec[3] = {loc[0], loc[1], 0.0};
264         int add_index;
265         int i;
266
267         pc = br->paint_curve;
268         if (!pc) {
269                 br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve");
270         }
271
272         paintcurve_undo_begin(C, op, pc);
273
274         pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint");
275         add_index = pc->add_index;
276
277         if (pc->points) {
278                 if (add_index > 0)
279                         memcpy(pcp, pc->points, add_index * sizeof(PaintCurvePoint));
280                 if (add_index < pc->tot_points)
281                         memcpy(pcp + add_index + 1, pc->points + add_index, (pc->tot_points - add_index) * sizeof(PaintCurvePoint));
282
283                 MEM_freeN(pc->points);
284         }
285         pc->points = pcp;
286         pc->tot_points++;
287
288         /* initialize new point */
289         memset(&pcp[add_index], 0, sizeof(PaintCurvePoint));
290         copy_v3_v3(pcp[add_index].bez.vec[0], vec);
291         copy_v3_v3(pcp[add_index].bez.vec[1], vec);
292         copy_v3_v3(pcp[add_index].bez.vec[2], vec);
293
294         /* last step, clear selection from all bezier handles expect the next */
295         for (i = 0; i < pc->tot_points; i++) {
296                 pcp[i].bez.f1 = pcp[i].bez.f2 = pcp[i].bez.f3 = 0;
297         }
298         pcp[add_index].bez.f3 = SELECT;
299         pcp[add_index].bez.h2 = HD_ALIGN;
300
301         pc->add_index = add_index + 1;
302
303         WM_paint_cursor_tag_redraw(window, ar);
304 }
305
306
307 static int paintcurve_add_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
308 {
309         int loc[2] = {event->mval[0], event->mval[1]};
310         paintcurve_point_add(C, op, loc);
311         RNA_int_set_array(op->ptr, "location", loc);
312         return OPERATOR_FINISHED;
313 }
314
315 static int paintcurve_add_point_exec(bContext *C, wmOperator *op)
316 {
317         int loc[2];
318
319         if (RNA_struct_property_is_set(op->ptr, "location")) {
320                 RNA_int_get_array(op->ptr, "location", loc);
321                 paintcurve_point_add(C, op, loc);
322                 return OPERATOR_FINISHED;
323         }
324
325         return OPERATOR_CANCELLED;
326 }
327
328 void PAINTCURVE_OT_add_point(wmOperatorType *ot)
329 {
330         /* identifiers */
331         ot->name = "Add New Paint Curve Point";
332         ot->description = "Add new paint curve point";
333         ot->idname = "PAINTCURVE_OT_add_point";
334
335         /* api callbacks */
336         ot->invoke = paintcurve_add_point_invoke;
337         ot->exec = paintcurve_add_point_exec;
338         ot->poll = paint_curve_poll;
339
340         /* flags */
341         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
342
343         /* properties */
344         RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX,
345                            "Location", "Location of vertex in area space", 0, SHRT_MAX);
346 }
347
348 static int paintcurve_delete_point_exec(bContext *C, wmOperator *op)
349 {
350         Paint *p = BKE_paint_get_active_from_context(C);
351         Brush *br = p->brush;
352         PaintCurve *pc;
353         PaintCurvePoint *pcp;
354         wmWindow *window = CTX_wm_window(C);
355         ARegion *ar = CTX_wm_region(C);
356         int i;
357         int tot_del = 0;
358         pc = br->paint_curve;
359
360         if (!pc || pc->tot_points == 0) {
361                 return OPERATOR_CANCELLED;
362         }
363
364         paintcurve_undo_begin(C, op, pc);
365
366 #define DELETE_TAG 2
367
368         for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) {
369                 if ((pcp->bez.f1 & SELECT) || (pcp->bez.f2 & SELECT) || (pcp->bez.f3 & SELECT)) {
370                         pcp->bez.f2 |= DELETE_TAG;
371                         tot_del++;
372                 }
373         }
374
375         if (tot_del > 0) {
376                 int j = 0;
377                 int new_tot = pc->tot_points - tot_del;
378                 PaintCurvePoint *points_new = NULL;
379                 if (new_tot > 0)
380                         points_new = MEM_mallocN(new_tot * sizeof(PaintCurvePoint), "PaintCurvePoint");
381
382                 for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) {
383                         if (!(pcp->bez.f2 & DELETE_TAG)) {
384                                 points_new[j] = pc->points[i];
385
386                                 if ((i + 1) == pc->add_index) {
387                                         pc->add_index = j + 1;
388                                 }
389                                 j++;
390                         }
391                         else if ((i + 1) == pc->add_index) {
392                                 /* prefer previous point */
393                                 pc->add_index = j;
394                         }
395                 }
396                 MEM_freeN(pc->points);
397
398                 pc->points = points_new;
399                 pc->tot_points = new_tot;
400         }
401
402 #undef DELETE_TAG
403
404         WM_paint_cursor_tag_redraw(window, ar);
405
406         return OPERATOR_FINISHED;
407 }
408
409
410 void PAINTCURVE_OT_delete_point(wmOperatorType *ot)
411 {
412         /* identifiers */
413         ot->name = "Add New Paint Curve Point";
414         ot->description = "Add new paint curve point";
415         ot->idname = "PAINTCURVE_OT_delete_point";
416
417         /* api callbacks */
418         ot->exec = paintcurve_delete_point_exec;
419         ot->poll = paint_curve_poll;
420
421         /* flags */
422         ot->flag = OPTYPE_UNDO;
423 }
424
425
426 static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2], bool toggle, bool extend)
427 {
428         wmWindow *window = CTX_wm_window(C);
429         ARegion *ar = CTX_wm_region(C);
430         Paint *p = BKE_paint_get_active_from_context(C);
431         Brush *br = p->brush;
432         PaintCurve *pc;
433         PaintCurvePoint *pcp;
434         int i;
435         const float loc_fl[2] = {UNPACK2(loc)};
436
437         pc = br->paint_curve;
438
439         if (!pc)
440                 return false;
441
442         paintcurve_undo_begin(C, op, pc);
443
444         pcp = pc->points;
445
446         if (toggle) {
447                 char select = 0;
448                 bool selected = false;
449
450                 for (i = 0; i < pc->tot_points; i++) {
451                         if (pcp[i].bez.f1 || pcp[i].bez.f2 || pcp[i].bez.f3) {
452                                 selected = true;
453                                 break;
454                         }
455                 }
456
457                 if (!selected) {
458                         select = SELECT;
459                 }
460
461                 for (i = 0; i < pc->tot_points; i++) {
462                         pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = select;
463                 }
464         }
465         else {
466                 PaintCurvePoint *pcp;
467                 char selflag;
468
469                 pcp = paintcurve_point_get_closest(pc, loc_fl, false, PAINT_CURVE_SELECT_THRESHOLD, &selflag);
470
471                 if (pcp) {
472                         pc->add_index = (pcp - pc->points) + 1;
473
474                         if (selflag == SEL_F2) {
475                                 if (extend)
476                                         pcp->bez.f2 ^= SELECT;
477                                 else
478                                         pcp->bez.f2 |= SELECT;
479                         }
480                         else if (selflag == SEL_F1) {
481                                 if (extend)
482                                         pcp->bez.f1 ^= SELECT;
483                                 else
484                                         pcp->bez.f1 |= SELECT;
485                         }
486                         else if (selflag == SEL_F3) {
487                                 if (extend)
488                                         pcp->bez.f3 ^= SELECT;
489                                 else
490                                         pcp->bez.f3 |= SELECT;
491                         }
492                 }
493
494                 /* clear selection for unselected points if not extending and if a point has been selected */
495                 if (!extend && pcp) {
496                         for (i = 0; i < pc->tot_points; i++) {
497                                 pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = 0;
498
499                                 if ((pc->points + i) == pcp) {
500                                         char index = paintcurve_point_co_index(selflag);
501                                         PAINT_CURVE_POINT_SELECT(pcp, index);
502                                 }
503                         }
504                 }
505
506                 if (!pcp)
507                         return false;
508         }
509
510         WM_paint_cursor_tag_redraw(window, ar);
511
512         return true;
513 }
514
515
516 static int paintcurve_select_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
517 {
518         int loc[2] = {UNPACK2(event->mval)};
519         bool toggle = RNA_boolean_get(op->ptr, "toggle");
520         bool extend = RNA_boolean_get(op->ptr, "extend");
521         if (paintcurve_point_select(C, op, loc, toggle, extend)) {
522                 RNA_int_set_array(op->ptr, "location", loc);
523                 return OPERATOR_FINISHED;
524         }
525         else {
526                 return OPERATOR_CANCELLED;
527         }
528 }
529
530 static int paintcurve_select_point_exec(bContext *C, wmOperator *op)
531 {
532         int loc[2];
533
534         if (RNA_struct_property_is_set(op->ptr, "location")) {
535                 bool toggle = RNA_boolean_get(op->ptr, "toggle");
536                 bool extend = RNA_boolean_get(op->ptr, "extend");
537                 RNA_int_get_array(op->ptr, "location", loc);
538                 if (paintcurve_point_select(C, op, loc, toggle, extend))
539                         return OPERATOR_FINISHED;
540         }
541
542         return OPERATOR_CANCELLED;
543 }
544
545 void PAINTCURVE_OT_select(wmOperatorType *ot)
546 {
547         PropertyRNA *prop;
548
549         /* identifiers */
550         ot->name = "Select Paint Curve Point";
551         ot->description = "Select a paint curve point";
552         ot->idname = "PAINTCURVE_OT_select";
553
554         /* api callbacks */
555         ot->invoke = paintcurve_select_point_invoke;
556         ot->exec = paintcurve_select_point_exec;
557         ot->poll = paint_curve_poll;
558
559         /* flags */
560         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
561
562         /* properties */
563         RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX,
564                            "Location", "Location of vertex in area space", 0, SHRT_MAX);
565         prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "Select/Deselect all");
566         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
567         prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection");
568         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
569 }
570
571 typedef struct PointSlideData {
572         PaintCurvePoint *pcp;
573         char select;
574         int initial_loc[2];
575         float point_initial_loc[3][2];
576         int event;
577         bool align;
578 } PointSlideData;
579
580 static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *event)
581 {
582         Paint *p = BKE_paint_get_active_from_context(C);
583         const float loc_fl[2] = {UNPACK2(event->mval)};
584         char select;
585         int i;
586         bool do_select = RNA_boolean_get(op->ptr, "select");
587         bool align = RNA_boolean_get(op->ptr, "align");
588         Brush *br = p->brush;
589         PaintCurve *pc = br->paint_curve;
590         PaintCurvePoint *pcp;
591
592         if (!pc)
593                 return OPERATOR_PASS_THROUGH;
594
595         if (do_select) {
596                 pcp = paintcurve_point_get_closest(pc, loc_fl, align, PAINT_CURVE_SELECT_THRESHOLD, &select);
597         }
598         else {
599                 /* just find first selected point */
600                 for (i = 0; i < pc->tot_points; i++) {
601                         if (pc->points[i].bez.f1 || pc->points[i].bez.f2 || pc->points[i].bez.f3) {
602                                 pcp = &pc->points[i];
603                                 select = SEL_F3;
604                                 break;
605                         }
606                 }
607         }
608
609
610         if (pcp) {
611                 ARegion *ar = CTX_wm_region(C);
612                 wmWindow *window = CTX_wm_window(C);
613                 PointSlideData *psd = MEM_mallocN(sizeof(PointSlideData), "PointSlideData");
614                 copy_v2_v2_int(psd->initial_loc, event->mval);
615                 psd->event = event->type;
616                 psd->pcp = pcp;
617                 psd->select = paintcurve_point_co_index(select);
618                 for (i = 0; i < 3; i++) {
619                         copy_v2_v2(psd->point_initial_loc[i], pcp->bez.vec[i]);
620                 }
621                 psd->align = align;
622                 op->customdata = psd;
623
624                 if (do_select)
625                         paintcurve_undo_begin(C, op, pc);
626
627                 /* first, clear all selection from points */
628                 for (i = 0; i < pc->tot_points; i++)
629                         pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0;
630
631                 /* only select the active point */
632                 PAINT_CURVE_POINT_SELECT(pcp, psd->select);
633                 pc->add_index = (pcp - pc->points) + 1;
634
635                 WM_event_add_modal_handler(C, op);
636                 WM_paint_cursor_tag_redraw(window, ar);
637                 return OPERATOR_RUNNING_MODAL;
638         }
639
640         return OPERATOR_PASS_THROUGH;
641 }
642
643 static int paintcurve_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
644 {
645         PointSlideData *psd = op->customdata;
646
647         if (event->type == psd->event && event->val == KM_RELEASE) {
648                 MEM_freeN(psd);
649                 return OPERATOR_FINISHED;
650         }
651
652         switch (event->type) {
653                 case MOUSEMOVE:
654                 {
655                         ARegion *ar = CTX_wm_region(C);
656                         wmWindow *window = CTX_wm_window(C);
657                         float diff[2] = {event->mval[0] - psd->initial_loc[0],
658                                          event->mval[1] - psd->initial_loc[1]};
659                         if (psd->select == 1) {
660                                 int i;
661                                 for (i = 0; i < 3; i++)
662                                         add_v2_v2v2(psd->pcp->bez.vec[i], diff, psd->point_initial_loc[i]);
663                         }
664                         else {
665                                 add_v2_v2(diff, psd->point_initial_loc[psd->select]);
666                                 copy_v2_v2(psd->pcp->bez.vec[psd->select], diff);
667
668                                 if (psd->align) {
669                                         char opposite = (psd->select == 0) ? 2 : 0;
670                                         sub_v2_v2v2(diff, psd->pcp->bez.vec[1], psd->pcp->bez.vec[psd->select]);
671                                         add_v2_v2v2(psd->pcp->bez.vec[opposite], psd->pcp->bez.vec[1], diff);
672                                 }
673                         }
674                         WM_paint_cursor_tag_redraw(window, ar);
675                         break;
676                 }
677                 default:
678                         break;
679         }
680
681         return OPERATOR_RUNNING_MODAL;
682 }
683
684
685 void PAINTCURVE_OT_slide(wmOperatorType *ot)
686 {
687         /* identifiers */
688         ot->name = "Slide Paint Curve Point";
689         ot->description = "Select and slide paint curve point";
690         ot->idname = "PAINTCURVE_OT_slide";
691
692         /* api callbacks */
693         ot->invoke = paintcurve_slide_invoke;
694         ot->modal = paintcurve_slide_modal;
695         ot->poll = paint_curve_poll;
696
697         /* flags */
698         ot->flag = OPTYPE_UNDO;
699
700         /* properties */
701         RNA_def_boolean(ot->srna, "align", false, "Align Handles", "Aligns opposite point handle during transform");
702         RNA_def_boolean(ot->srna, "select", true, "Select", "Attempt to select a point handle before transform");
703 }
704
705 static int paintcurve_draw_exec(bContext *C, wmOperator *UNUSED(op))
706 {
707         PaintMode mode = BKE_paintmode_get_active_from_context(C);
708         const char *name;
709
710         switch (mode) {
711                 case PAINT_TEXTURE_2D:
712                 case PAINT_TEXTURE_PROJECTIVE:
713                         name = "PAINT_OT_image_paint";
714                         break;
715                 case PAINT_WEIGHT:
716                         name = "PAINT_OT_weight_paint";
717                         break;
718                 case PAINT_VERTEX:
719                         name = "PAINT_OT_vertex_paint";
720                         break;
721                 case PAINT_SCULPT:
722                         name = "SCULPT_OT_brush_stroke";
723                         break;
724                 default:
725                         return OPERATOR_PASS_THROUGH;
726         }
727
728         return WM_operator_name_call(C, name, WM_OP_INVOKE_DEFAULT, NULL);
729 }
730
731 void PAINTCURVE_OT_draw(wmOperatorType *ot)
732 {
733         /* identifiers */
734         ot->name = "Draw Curve";
735         ot->description = "Draw curve";
736         ot->idname = "PAINTCURVE_OT_draw";
737
738         /* api callbacks */
739         ot->exec = paintcurve_draw_exec;
740         ot->poll = paint_curve_poll;
741
742         /* flags */
743         ot->flag = OPTYPE_UNDO;
744 }
745
746 static int paintcurve_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
747 {
748         op->customdata = SET_INT_IN_POINTER(event->type);
749         WM_event_add_modal_handler(C, op);
750
751         return OPERATOR_RUNNING_MODAL;
752 }
753
754 static int paintcurve_cursor_modal(bContext *C, wmOperator *op, const wmEvent *event)
755 {
756         if (event->type == GET_INT_FROM_POINTER(op->customdata) && event->val == KM_RELEASE)
757                 return OPERATOR_FINISHED;
758
759         if (event->type == MOUSEMOVE) {
760                 PaintMode mode = BKE_paintmode_get_active_from_context(C);
761
762                 switch (mode) {
763                         case PAINT_TEXTURE_2D:
764                         {
765                                 ARegion *ar = CTX_wm_region(C);
766                                 SpaceImage *sima = CTX_wm_space_image(C);
767                                 float location[2];
768
769                                 if (!sima)
770                                         return OPERATOR_CANCELLED;
771
772                                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
773                                 copy_v2_v2(sima->cursor, location);
774                                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
775                                 break;
776                         }
777                         default:
778                                 ED_view3d_cursor3d_update(C, event->mval);
779                                 break;
780                 }
781         }
782
783         return OPERATOR_RUNNING_MODAL;
784 }
785
786 void PAINTCURVE_OT_cursor(wmOperatorType *ot)
787 {
788         /* identifiers */
789         ot->name = "Place Cursor";
790         ot->description = "Place cursor";
791         ot->idname = "PAINTCURVE_OT_cursor";
792
793         /* api callbacks */
794         ot->invoke = paintcurve_cursor_invoke;
795         ot->modal = paintcurve_cursor_modal;
796         ot->poll = paint_curve_poll;
797
798         /* flags */
799         ot->flag = 0;
800 }