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