1bbc1a7a555805e3d00023112dd6993fdea7c882
[blender.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 "BLI_math_vector.h"
37
38 #include "BKE_context.h"
39 #include "BKE_main.h"
40 #include "BKE_paint.h"
41
42 #include "ED_view3d.h"
43 #include "ED_paint.h"
44
45 #include "WM_api.h"
46 #include "WM_types.h"
47
48 #include "RNA_access.h"
49 #include "RNA_define.h"
50
51 #include "UI_view2d.h"
52
53 #include "paint_intern.h"
54
55 #define PAINT_CURVE_SELECT_THRESHOLD 40.0f
56 #define PAINT_CURVE_POINT_SELECT(pcp, i) (*(&pcp->bez.f1 + i) = SELECT)
57
58 bool paint_curve_poll(bContext *C)
59 {
60         Object *ob = CTX_data_active_object(C);
61         Paint *p;
62         RegionView3D *rv3d = CTX_wm_region_view3d(C);
63         SpaceImage *sima;
64
65         if (rv3d && !(ob && ((ob->mode & OB_MODE_ALL_PAINT) != 0)))
66                 return false;
67
68         sima = CTX_wm_space_image(C);
69
70         if (sima && sima->mode != SI_MODE_PAINT)
71                 return false;
72
73         p = BKE_paint_get_active_from_context(C);
74
75         if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) {
76                 return true;
77         }
78
79         return false;
80 }
81
82 #define SEL_F1 (1 << 0)
83 #define SEL_F2 (1 << 1)
84 #define SEL_F3 (1 << 2)
85
86 /* returns 0, 1, or 2 in point according to handle 1, pivot or handle 2 */
87 static PaintCurvePoint *paintcurve_point_get_closest(PaintCurve *pc, const float pos[2], bool ignore_pivot, const float threshold, char *point)
88 {
89         PaintCurvePoint *pcp, *closest = NULL;
90         int i;
91         float dist, closest_dist = FLT_MAX;
92
93         for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) {
94                 dist = len_manhattan_v2v2(pos, pcp->bez.vec[0]);
95                 if (dist < threshold) {
96                         if (dist < closest_dist) {
97                                 closest = pcp;
98                                 closest_dist = dist;
99                                 if (point)
100                                         *point = SEL_F1;
101                         }
102                 }
103                 if (!ignore_pivot) {
104                         dist = len_manhattan_v2v2(pos, pcp->bez.vec[1]);
105                         if (dist < threshold) {
106                                 if (dist < closest_dist) {
107                                         closest = pcp;
108                                         closest_dist = dist;
109                                         if (point)
110                                                 *point = SEL_F2;
111                                 }
112                         }
113                 }
114                 dist = len_manhattan_v2v2(pos, pcp->bez.vec[2]);
115                 if (dist < threshold) {
116                         if (dist < closest_dist) {
117                                 closest = pcp;
118                                 closest_dist = dist;
119                                 if (point)
120                                         *point = SEL_F3;
121                         }
122                 }
123         }
124
125         return closest;
126 }
127
128 static int paintcurve_point_co_index(char sel)
129 {
130         char i = 0;
131         while (sel != 1) {
132                 sel >>= 1;
133                 i++;
134         }
135         return i;
136 }
137
138 static char paintcurve_point_side_index(const BezTriple *bezt, const bool is_first, const char fallback)
139 {
140         /* when matching, guess based on endpoint side */
141         if (BEZT_ISSEL_ANY(bezt)) {
142                 if ((bezt->f1 & SELECT) == (bezt->f3 & SELECT)) {
143                         return is_first ? SEL_F1 : SEL_F3;
144                 }
145                 else if (bezt->f1 & SELECT) {
146                         return SEL_F1;
147                 }
148                 else if (bezt->f3  & SELECT) {
149                         return SEL_F3;
150                 }
151                 else {
152                         return fallback;
153                 }
154         }
155         else {
156                 return 0;
157         }
158 }
159
160 /******************* Operators *********************************/
161
162 static int paintcurve_new_exec(bContext *C, wmOperator *UNUSED(op))
163 {
164         Paint *p = BKE_paint_get_active_from_context(C);
165         Main *bmain = CTX_data_main(C);
166
167         if (p && p->brush) {
168                 p->brush->paint_curve = BKE_paint_curve_add(bmain, "PaintCurve");
169         }
170
171         return OPERATOR_FINISHED;
172 }
173
174 void PAINTCURVE_OT_new(wmOperatorType *ot)
175 {
176         /* identifiers */
177         ot->name = "Add New Paint Curve";
178         ot->description = "Add new paint curve";
179         ot->idname = "PAINTCURVE_OT_new";
180
181         /* api callbacks */
182         ot->exec = paintcurve_new_exec;
183         ot->poll = paint_curve_poll;
184
185         /* flags */
186         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
187 }
188
189 static void paintcurve_point_add(bContext *C,  wmOperator *op, const int loc[2])
190 {
191         Paint *p = BKE_paint_get_active_from_context(C);
192         Brush *br = p->brush;
193         Main *bmain = CTX_data_main(C);
194         PaintCurve *pc;
195         PaintCurvePoint *pcp;
196         wmWindow *window = CTX_wm_window(C);
197         ARegion *ar = CTX_wm_region(C);
198         float vec[3] = {loc[0], loc[1], 0.0};
199         int add_index;
200         int i;
201
202         pc = br->paint_curve;
203         if (!pc) {
204                 br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve");
205         }
206
207         ED_paintcurve_undo_push_begin(op->type->name);
208
209         pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint");
210         add_index = pc->add_index;
211
212         if (pc->points) {
213                 if (add_index > 0)
214                         memcpy(pcp, pc->points, add_index * sizeof(PaintCurvePoint));
215                 if (add_index < pc->tot_points)
216                         memcpy(pcp + add_index + 1, pc->points + add_index, (pc->tot_points - add_index) * sizeof(PaintCurvePoint));
217
218                 MEM_freeN(pc->points);
219         }
220         pc->points = pcp;
221         pc->tot_points++;
222
223         /* initialize new point */
224         memset(&pcp[add_index], 0, sizeof(PaintCurvePoint));
225         copy_v3_v3(pcp[add_index].bez.vec[0], vec);
226         copy_v3_v3(pcp[add_index].bez.vec[1], vec);
227         copy_v3_v3(pcp[add_index].bez.vec[2], vec);
228
229         /* last step, clear selection from all bezier handles expect the next */
230         for (i = 0; i < pc->tot_points; i++) {
231                 pcp[i].bez.f1 = pcp[i].bez.f2 = pcp[i].bez.f3 = 0;
232         }
233
234         BKE_paint_curve_clamp_endpoint_add_index(pc, add_index);
235
236         if (pc->add_index != 0) {
237                 pcp[add_index].bez.f3 = SELECT;
238                 pcp[add_index].bez.h2 = HD_ALIGN;
239         }
240         else {
241                 pcp[add_index].bez.f1 = SELECT;
242                 pcp[add_index].bez.h1 = HD_ALIGN;
243         }
244
245         ED_paintcurve_undo_push_end();
246
247         WM_paint_cursor_tag_redraw(window, ar);
248 }
249
250
251 static int paintcurve_add_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
252 {
253         int loc[2] = {event->mval[0], event->mval[1]};
254         paintcurve_point_add(C, op, loc);
255         RNA_int_set_array(op->ptr, "location", loc);
256         return OPERATOR_FINISHED;
257 }
258
259 static int paintcurve_add_point_exec(bContext *C, wmOperator *op)
260 {
261         int loc[2];
262
263         if (RNA_struct_property_is_set(op->ptr, "location")) {
264                 RNA_int_get_array(op->ptr, "location", loc);
265                 paintcurve_point_add(C, op, loc);
266                 return OPERATOR_FINISHED;
267         }
268
269         return OPERATOR_CANCELLED;
270 }
271
272 void PAINTCURVE_OT_add_point(wmOperatorType *ot)
273 {
274         /* identifiers */
275         ot->name = "Add New Paint Curve Point";
276         ot->description = ot->name;
277         ot->idname = "PAINTCURVE_OT_add_point";
278
279         /* api callbacks */
280         ot->invoke = paintcurve_add_point_invoke;
281         ot->exec = paintcurve_add_point_exec;
282         ot->poll = paint_curve_poll;
283
284         /* flags */
285         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
286
287         /* properties */
288         RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX,
289                            "Location", "Location of vertex in area space", 0, SHRT_MAX);
290 }
291
292 static int paintcurve_delete_point_exec(bContext *C, wmOperator *op)
293 {
294         Paint *p = BKE_paint_get_active_from_context(C);
295         Brush *br = p->brush;
296         PaintCurve *pc;
297         PaintCurvePoint *pcp;
298         wmWindow *window = CTX_wm_window(C);
299         ARegion *ar = CTX_wm_region(C);
300         int i;
301         int tot_del = 0;
302         pc = br->paint_curve;
303
304         if (!pc || pc->tot_points == 0) {
305                 return OPERATOR_CANCELLED;
306         }
307
308         ED_paintcurve_undo_push_begin(op->type->name);
309
310 #define DELETE_TAG 2
311
312         for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) {
313                 if (BEZT_ISSEL_ANY(&pcp->bez)) {
314                         pcp->bez.f2 |= DELETE_TAG;
315                         tot_del++;
316                 }
317         }
318
319         if (tot_del > 0) {
320                 int j = 0;
321                 int new_tot = pc->tot_points - tot_del;
322                 PaintCurvePoint *points_new = NULL;
323                 if (new_tot > 0)
324                         points_new = MEM_mallocN(new_tot * sizeof(PaintCurvePoint), "PaintCurvePoint");
325
326                 for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) {
327                         if (!(pcp->bez.f2 & DELETE_TAG)) {
328                                 points_new[j] = pc->points[i];
329
330                                 if ((i + 1) == pc->add_index) {
331                                         BKE_paint_curve_clamp_endpoint_add_index(pc, j);
332                                 }
333                                 j++;
334                         }
335                         else if ((i + 1) == pc->add_index) {
336                                 /* prefer previous point */
337                                 pc->add_index = j;
338                         }
339                 }
340                 MEM_freeN(pc->points);
341
342                 pc->points = points_new;
343                 pc->tot_points = new_tot;
344         }
345
346 #undef DELETE_TAG
347
348         ED_paintcurve_undo_push_end();
349
350         WM_paint_cursor_tag_redraw(window, ar);
351
352         return OPERATOR_FINISHED;
353 }
354
355
356 void PAINTCURVE_OT_delete_point(wmOperatorType *ot)
357 {
358         /* identifiers */
359         ot->name = "Remove Paint Curve Point";
360         ot->description = ot->name;
361         ot->idname = "PAINTCURVE_OT_delete_point";
362
363         /* api callbacks */
364         ot->exec = paintcurve_delete_point_exec;
365         ot->poll = paint_curve_poll;
366
367         /* flags */
368         ot->flag = OPTYPE_UNDO;
369 }
370
371
372 static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2], bool toggle, bool extend)
373 {
374         wmWindow *window = CTX_wm_window(C);
375         ARegion *ar = CTX_wm_region(C);
376         Paint *p = BKE_paint_get_active_from_context(C);
377         Brush *br = p->brush;
378         PaintCurve *pc;
379         int i;
380         const float loc_fl[2] = {UNPACK2(loc)};
381
382         pc = br->paint_curve;
383
384         if (!pc)
385                 return false;
386
387         ED_paintcurve_undo_push_begin(op->type->name);
388
389         if (toggle) {
390                 PaintCurvePoint *pcp;
391                 char select = 0;
392                 bool selected = false;
393
394                 pcp = pc->points;
395
396                 for (i = 0; i < pc->tot_points; i++) {
397                         if (pcp[i].bez.f1 || pcp[i].bez.f2 || pcp[i].bez.f3) {
398                                 selected = true;
399                                 break;
400                         }
401                 }
402
403                 if (!selected) {
404                         select = SELECT;
405                 }
406
407                 for (i = 0; i < pc->tot_points; i++) {
408                         pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = select;
409                 }
410         }
411         else {
412                 PaintCurvePoint *pcp;
413                 char selflag;
414
415                 pcp = paintcurve_point_get_closest(pc, loc_fl, false, PAINT_CURVE_SELECT_THRESHOLD, &selflag);
416
417                 if (pcp) {
418                         BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points);
419
420                         if (selflag == SEL_F2) {
421                                 if (extend)
422                                         pcp->bez.f2 ^= SELECT;
423                                 else
424                                         pcp->bez.f2 |= SELECT;
425                         }
426                         else if (selflag == SEL_F1) {
427                                 if (extend)
428                                         pcp->bez.f1 ^= SELECT;
429                                 else
430                                         pcp->bez.f1 |= SELECT;
431                         }
432                         else if (selflag == SEL_F3) {
433                                 if (extend)
434                                         pcp->bez.f3 ^= SELECT;
435                                 else
436                                         pcp->bez.f3 |= SELECT;
437                         }
438                 }
439
440                 /* clear selection for unselected points if not extending and if a point has been selected */
441                 if (!extend && pcp) {
442                         for (i = 0; i < pc->tot_points; i++) {
443                                 pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = 0;
444
445                                 if ((pc->points + i) == pcp) {
446                                         char index = paintcurve_point_co_index(selflag);
447                                         PAINT_CURVE_POINT_SELECT(pcp, index);
448                                 }
449                         }
450                 }
451
452                 if (!pcp) {
453                         ED_paintcurve_undo_push_end();
454                         return false;
455                 }
456         }
457
458         ED_paintcurve_undo_push_end();
459
460         WM_paint_cursor_tag_redraw(window, ar);
461
462         return true;
463 }
464
465
466 static int paintcurve_select_point_invoke(bContext *C, wmOperator *op, const wmEvent *event)
467 {
468         int loc[2] = {UNPACK2(event->mval)};
469         bool toggle = RNA_boolean_get(op->ptr, "toggle");
470         bool extend = RNA_boolean_get(op->ptr, "extend");
471         if (paintcurve_point_select(C, op, loc, toggle, extend)) {
472                 RNA_int_set_array(op->ptr, "location", loc);
473                 return OPERATOR_FINISHED;
474         }
475         else {
476                 return OPERATOR_CANCELLED;
477         }
478 }
479
480 static int paintcurve_select_point_exec(bContext *C, wmOperator *op)
481 {
482         int loc[2];
483
484         if (RNA_struct_property_is_set(op->ptr, "location")) {
485                 bool toggle = RNA_boolean_get(op->ptr, "toggle");
486                 bool extend = RNA_boolean_get(op->ptr, "extend");
487                 RNA_int_get_array(op->ptr, "location", loc);
488                 if (paintcurve_point_select(C, op, loc, toggle, extend))
489                         return OPERATOR_FINISHED;
490         }
491
492         return OPERATOR_CANCELLED;
493 }
494
495 void PAINTCURVE_OT_select(wmOperatorType *ot)
496 {
497         PropertyRNA *prop;
498
499         /* identifiers */
500         ot->name = "Select Paint Curve Point";
501         ot->description = "Select a paint curve point";
502         ot->idname = "PAINTCURVE_OT_select";
503
504         /* api callbacks */
505         ot->invoke = paintcurve_select_point_invoke;
506         ot->exec = paintcurve_select_point_exec;
507         ot->poll = paint_curve_poll;
508
509         /* flags */
510         ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
511
512         /* properties */
513         RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX,
514                            "Location", "Location of vertex in area space", 0, SHRT_MAX);
515         prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "(De)select all");
516         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
517         prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection");
518         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
519 }
520
521 typedef struct PointSlideData {
522         PaintCurvePoint *pcp;
523         char select;
524         int initial_loc[2];
525         float point_initial_loc[3][2];
526         int event;
527         bool align;
528 } PointSlideData;
529
530 static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *event)
531 {
532         Paint *p = BKE_paint_get_active_from_context(C);
533         const float loc_fl[2] = {UNPACK2(event->mval)};
534         char select;
535         int i;
536         bool do_select = RNA_boolean_get(op->ptr, "select");
537         bool align = RNA_boolean_get(op->ptr, "align");
538         Brush *br = p->brush;
539         PaintCurve *pc = br->paint_curve;
540         PaintCurvePoint *pcp;
541
542         if (!pc)
543                 return OPERATOR_PASS_THROUGH;
544
545         if (do_select) {
546                 pcp = paintcurve_point_get_closest(pc, loc_fl, align, PAINT_CURVE_SELECT_THRESHOLD, &select);
547         }
548         else {
549                 pcp = NULL;
550                 /* just find first selected point */
551                 for (i = 0; i < pc->tot_points; i++) {
552                         if ((select = paintcurve_point_side_index(&pc->points[i].bez, i == 0, SEL_F3))) {
553                                 pcp = &pc->points[i];
554                                 break;
555                         }
556                 }
557         }
558
559
560         if (pcp) {
561                 ARegion *ar = CTX_wm_region(C);
562                 wmWindow *window = CTX_wm_window(C);
563                 PointSlideData *psd = MEM_mallocN(sizeof(PointSlideData), "PointSlideData");
564                 copy_v2_v2_int(psd->initial_loc, event->mval);
565                 psd->event = event->type;
566                 psd->pcp = pcp;
567                 psd->select = paintcurve_point_co_index(select);
568                 for (i = 0; i < 3; i++) {
569                         copy_v2_v2(psd->point_initial_loc[i], pcp->bez.vec[i]);
570                 }
571                 psd->align = align;
572                 op->customdata = psd;
573
574                 /* first, clear all selection from points */
575                 for (i = 0; i < pc->tot_points; i++)
576                         pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0;
577
578                 /* only select the active point */
579                 PAINT_CURVE_POINT_SELECT(pcp, psd->select);
580                 BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points);
581
582                 WM_event_add_modal_handler(C, op);
583                 WM_paint_cursor_tag_redraw(window, ar);
584                 return OPERATOR_RUNNING_MODAL;
585         }
586
587         return OPERATOR_PASS_THROUGH;
588 }
589
590 static int paintcurve_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
591 {
592         PointSlideData *psd = op->customdata;
593
594         if (event->type == psd->event && event->val == KM_RELEASE) {
595                 MEM_freeN(psd);
596                 ED_paintcurve_undo_push_begin(op->type->name);
597                 ED_paintcurve_undo_push_end();
598                 return OPERATOR_FINISHED;
599         }
600
601         switch (event->type) {
602                 case MOUSEMOVE:
603                 {
604                         ARegion *ar = CTX_wm_region(C);
605                         wmWindow *window = CTX_wm_window(C);
606                         float diff[2] = {
607                                 event->mval[0] - psd->initial_loc[0],
608                                 event->mval[1] - psd->initial_loc[1]};
609                         if (psd->select == 1) {
610                                 int i;
611                                 for (i = 0; i < 3; i++)
612                                         add_v2_v2v2(psd->pcp->bez.vec[i], diff, psd->point_initial_loc[i]);
613                         }
614                         else {
615                                 add_v2_v2(diff, psd->point_initial_loc[psd->select]);
616                                 copy_v2_v2(psd->pcp->bez.vec[psd->select], diff);
617
618                                 if (psd->align) {
619                                         char opposite = (psd->select == 0) ? 2 : 0;
620                                         sub_v2_v2v2(diff, psd->pcp->bez.vec[1], psd->pcp->bez.vec[psd->select]);
621                                         add_v2_v2v2(psd->pcp->bez.vec[opposite], psd->pcp->bez.vec[1], diff);
622                                 }
623                         }
624                         WM_paint_cursor_tag_redraw(window, ar);
625                         break;
626                 }
627                 default:
628                         break;
629         }
630
631         return OPERATOR_RUNNING_MODAL;
632 }
633
634
635 void PAINTCURVE_OT_slide(wmOperatorType *ot)
636 {
637         /* identifiers */
638         ot->name = "Slide Paint Curve Point";
639         ot->description = "Select and slide paint curve point";
640         ot->idname = "PAINTCURVE_OT_slide";
641
642         /* api callbacks */
643         ot->invoke = paintcurve_slide_invoke;
644         ot->modal = paintcurve_slide_modal;
645         ot->poll = paint_curve_poll;
646
647         /* flags */
648         ot->flag = OPTYPE_UNDO;
649
650         /* properties */
651         RNA_def_boolean(ot->srna, "align", false, "Align Handles", "Aligns opposite point handle during transform");
652         RNA_def_boolean(ot->srna, "select", true, "Select", "Attempt to select a point handle before transform");
653 }
654
655 static int paintcurve_draw_exec(bContext *C, wmOperator *UNUSED(op))
656 {
657         ePaintMode mode = BKE_paintmode_get_active_from_context(C);
658         const char *name;
659
660         switch (mode) {
661                 case ePaintTexture2D:
662                 case ePaintTexture3D:
663                         name = "PAINT_OT_image_paint";
664                         break;
665                 case ePaintWeight:
666                         name = "PAINT_OT_weight_paint";
667                         break;
668                 case ePaintVertex:
669                         name = "PAINT_OT_vertex_paint";
670                         break;
671                 case ePaintSculpt:
672                         name = "SCULPT_OT_brush_stroke";
673                         break;
674                 default:
675                         return OPERATOR_PASS_THROUGH;
676         }
677
678         return WM_operator_name_call(C, name, WM_OP_INVOKE_DEFAULT, NULL);
679 }
680
681 void PAINTCURVE_OT_draw(wmOperatorType *ot)
682 {
683         /* identifiers */
684         ot->name = "Draw Curve";
685         ot->description = "Draw curve";
686         ot->idname = "PAINTCURVE_OT_draw";
687
688         /* api callbacks */
689         ot->exec = paintcurve_draw_exec;
690         ot->poll = paint_curve_poll;
691
692         /* flags */
693         ot->flag = OPTYPE_UNDO;
694 }
695
696 static int paintcurve_cursor_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
697 {
698         ePaintMode mode = BKE_paintmode_get_active_from_context(C);
699
700         switch (mode) {
701                 case ePaintTexture2D:
702                 {
703                         ARegion *ar = CTX_wm_region(C);
704                         SpaceImage *sima = CTX_wm_space_image(C);
705                         float location[2];
706
707                         if (!sima)
708                                 return OPERATOR_CANCELLED;
709
710                         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
711                         copy_v2_v2(sima->cursor, location);
712                         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
713                         break;
714                 }
715                 default:
716                         ED_view3d_cursor3d_update(C, event->mval);
717                         break;
718         }
719
720         return OPERATOR_FINISHED;
721 }
722
723 void PAINTCURVE_OT_cursor(wmOperatorType *ot)
724 {
725         /* identifiers */
726         ot->name = "Place Cursor";
727         ot->description = "Place cursor";
728         ot->idname = "PAINTCURVE_OT_cursor";
729
730         /* api callbacks */
731         ot->invoke = paintcurve_cursor_invoke;
732         ot->poll = paint_curve_poll;
733
734         /* flags */
735         ot->flag = 0;
736 }