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