Workbench: API Changes
[blender.git] / source / blender / editors / space_node / node_manipulators.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/space_node/node_manipulators.c
22  *  \ingroup spnode
23  */
24
25 #include <math.h>
26
27 #include "BLI_utildefines.h"
28 #include "BLI_math_matrix.h"
29 #include "BLI_math_vector.h"
30 #include "BLI_rect.h"
31
32 #include "BKE_context.h"
33 #include "BKE_image.h"
34
35 #include "ED_screen.h"
36 #include "ED_manipulator_library.h"
37
38 #include "IMB_imbuf_types.h"
39
40 #include "MEM_guardedalloc.h"
41
42 #include "RNA_access.h"
43
44 #include "WM_api.h"
45 #include "WM_types.h"
46
47 #include "node_intern.h"
48
49
50 /* -------------------------------------------------------------------- */
51
52 /** \name Local Utilities
53  * \{ */
54
55 static void node_manipulator_calc_matrix_space(
56         const SpaceNode *snode, const ARegion *ar, float matrix_space[4][4])
57 {
58         unit_m4(matrix_space);
59         mul_v3_fl(matrix_space[0], snode->zoom);
60         mul_v3_fl(matrix_space[1], snode->zoom);
61         matrix_space[3][0] = (ar->winx / 2) + snode->xof;
62         matrix_space[3][1] = (ar->winy / 2) + snode->yof;
63 }
64
65 static void node_manipulator_calc_matrix_space_with_image_dims(
66         const SpaceNode *snode, const ARegion *ar, const float image_dims[2], float matrix_space[4][4])
67 {
68         unit_m4(matrix_space);
69         mul_v3_fl(matrix_space[0], snode->zoom * image_dims[0]);
70         mul_v3_fl(matrix_space[1], snode->zoom * image_dims[1]);
71         matrix_space[3][0] = ((ar->winx / 2) + snode->xof) - ((image_dims[0] / 2.0f) * snode->zoom);
72         matrix_space[3][1] = ((ar->winy / 2) + snode->yof) - ((image_dims[1] / 2.0f) * snode->zoom);
73 }
74
75 /** \} */
76
77
78
79 /* -------------------------------------------------------------------- */
80
81 /** \name Backdrop Manipulator
82  * \{ */
83
84 static void manipulator_node_backdrop_prop_matrix_get(
85         const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop,
86         void *value_p)
87 {
88         float (*matrix)[4] = value_p;
89         BLI_assert(mpr_prop->type->array_length == 16);
90         const SpaceNode *snode = mpr_prop->custom_func.user_data;
91         matrix[0][0] = snode->zoom;
92         matrix[1][1] = snode->zoom;
93         matrix[3][0] = snode->xof;
94         matrix[3][1] = snode->yof;
95 }
96
97 static void manipulator_node_backdrop_prop_matrix_set(
98         const wmManipulator *UNUSED(mpr), wmManipulatorProperty *mpr_prop,
99         const void *value_p)
100 {
101         const float (*matrix)[4] = value_p;
102         BLI_assert(mpr_prop->type->array_length == 16);
103         SpaceNode *snode = mpr_prop->custom_func.user_data;
104         snode->zoom = matrix[0][0];
105         snode->zoom = matrix[1][1];
106         snode->xof  = matrix[3][0];
107         snode->yof  = matrix[3][1];
108 }
109
110 static bool WIDGETGROUP_node_transform_poll(const bContext *C, wmManipulatorGroupType *UNUSED(wgt))
111 {
112         SpaceNode *snode = CTX_wm_space_node(C);
113
114         if ((snode->flag & SNODE_BACKDRAW) == 0) {
115                 return false;
116         }
117
118         if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
119                 bNode *node = nodeGetActive(snode->edittree);
120
121                 if (node && ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
122                         return true;
123                 }
124         }
125
126         return false;
127 }
128
129 static void WIDGETGROUP_node_transform_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
130 {
131         wmManipulatorWrapper *wwrapper = MEM_mallocN(sizeof(wmManipulatorWrapper), __func__);
132
133         wwrapper->manipulator = WM_manipulator_new("MANIPULATOR_WT_cage_2d", mgroup, NULL);
134
135         RNA_enum_set(wwrapper->manipulator->ptr, "transform",
136                      ED_MANIPULATOR_CAGE2D_XFORM_FLAG_TRANSLATE | ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE_UNIFORM);
137
138         mgroup->customdata = wwrapper;
139 }
140
141 static void WIDGETGROUP_node_transform_refresh(const bContext *C, wmManipulatorGroup *mgroup)
142 {
143         wmManipulator *cage = ((wmManipulatorWrapper *)mgroup->customdata)->manipulator;
144         const ARegion *ar = CTX_wm_region(C);
145         /* center is always at the origin */
146         const float origin[3] = {ar->winx / 2, ar->winy / 2};
147
148         void *lock;
149         Image *ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
150         ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
151
152         if (ibuf) {
153                 const float dims[2] = {
154                         (ibuf->x > 0) ? ibuf->x : 64.0f,
155                         (ibuf->y > 0) ? ibuf->y : 64.0f,
156                 };
157
158                 RNA_float_set_array(cage->ptr, "dimensions", dims);
159                 WM_manipulator_set_matrix_location(cage, origin);
160                 WM_manipulator_set_flag(cage, WM_MANIPULATOR_HIDDEN, false);
161
162                 /* need to set property here for undo. TODO would prefer to do this in _init */
163                 SpaceNode *snode = CTX_wm_space_node(C);
164 #if 0
165                 PointerRNA nodeptr;
166                 RNA_pointer_create(snode->id, &RNA_SpaceNodeEditor, snode, &nodeptr);
167                 WM_manipulator_target_property_def_rna(cage, "offset", &nodeptr, "backdrop_offset", -1);
168                 WM_manipulator_target_property_def_rna(cage, "scale", &nodeptr, "backdrop_zoom", -1);
169 #endif
170
171                 WM_manipulator_target_property_def_func(
172                         cage, "matrix",
173                         &(const struct wmManipulatorPropertyFnParams) {
174                             .value_get_fn = manipulator_node_backdrop_prop_matrix_get,
175                             .value_set_fn = manipulator_node_backdrop_prop_matrix_set,
176                             .range_get_fn = NULL,
177                             .user_data = snode,
178                         });
179         }
180         else {
181                 WM_manipulator_set_flag(cage, WM_MANIPULATOR_HIDDEN, true);
182         }
183
184         BKE_image_release_ibuf(ima, ibuf, lock);
185 }
186
187 void NODE_WGT_backdrop_transform(wmManipulatorGroupType *wgt)
188 {
189         wgt->name = "Backdrop Transform Widget";
190         wgt->idname = "NODE_WGT_backdrop_transform";
191
192         wgt->flag |= WM_MANIPULATORGROUPTYPE_PERSISTENT;
193
194         wgt->poll = WIDGETGROUP_node_transform_poll;
195         wgt->setup = WIDGETGROUP_node_transform_setup;
196         wgt->refresh = WIDGETGROUP_node_transform_refresh;
197 }
198
199 /** \} */
200
201 /* -------------------------------------------------------------------- */
202
203 /** \name Crop Manipulator
204  * \{ */
205
206 struct NodeCropWidgetGroup {
207         wmManipulator *border;
208
209         struct {
210                 float dims[2];
211         } state;
212
213         struct {
214                 PointerRNA ptr;
215                 PropertyRNA *prop;
216                 bContext *context;
217         } update_data;
218 };
219
220 static void manipulator_node_crop_update(struct NodeCropWidgetGroup *crop_group)
221 {
222         RNA_property_update(crop_group->update_data.context, &crop_group->update_data.ptr, crop_group->update_data.prop);
223 }
224
225 static void two_xy_to_rect(const NodeTwoXYs *nxy, rctf *rect, const float dims[2], bool is_relative)
226 {
227         if (is_relative) {
228                 rect->xmin = nxy->fac_x1;
229                 rect->xmax = nxy->fac_x2;
230                 rect->ymin = nxy->fac_y1;
231                 rect->ymax = nxy->fac_y2;
232         }
233         else {
234                 rect->xmin = nxy->x1 / dims[0];
235                 rect->xmax = nxy->x2 / dims[0];
236                 rect->ymin = nxy->y1 / dims[1];
237                 rect->ymax = nxy->y2 / dims[1];
238         }
239 }
240
241 static void two_xy_from_rect(NodeTwoXYs *nxy, const rctf *rect, const float dims[2], bool is_relative)
242 {
243         if (is_relative) {
244                 nxy->fac_x1 = rect->xmin;
245                 nxy->fac_x2 = rect->xmax;
246                 nxy->fac_y1 = rect->ymin;
247                 nxy->fac_y2 = rect->ymax;
248         }
249         else {
250                 nxy->x1 = rect->xmin * dims[0];
251                 nxy->x2 = rect->xmax * dims[0];
252                 nxy->y1 = rect->ymin * dims[1];
253                 nxy->y2 = rect->ymax * dims[1];
254         }
255 }
256
257 /* scale callbacks */
258 static void manipulator_node_crop_prop_matrix_get(
259         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
260         void *value_p)
261 {
262         float (*matrix)[4] = value_p;
263         BLI_assert(mpr_prop->type->array_length == 16);
264         struct NodeCropWidgetGroup *crop_group = mpr->parent_mgroup->customdata;
265         const float *dims = crop_group->state.dims;
266         const bNode *node = mpr_prop->custom_func.user_data;
267         const NodeTwoXYs *nxy = node->storage;
268         bool is_relative = (bool)node->custom2;
269         rctf rct;
270         two_xy_to_rect(nxy, &rct, dims, is_relative);
271         matrix[0][0] = BLI_rctf_size_x(&rct);
272         matrix[1][1] = BLI_rctf_size_y(&rct);
273         matrix[3][0] = (BLI_rctf_cent_x(&rct) - 0.5f) * dims[0];
274         matrix[3][1] = (BLI_rctf_cent_y(&rct) - 0.5f) * dims[1];
275 }
276
277 static void manipulator_node_crop_prop_matrix_set(
278         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
279         const void *value_p)
280 {
281         const float (*matrix)[4] = value_p;
282         BLI_assert(mpr_prop->type->array_length == 16);
283         struct NodeCropWidgetGroup *crop_group = mpr->parent_mgroup->customdata;
284         const float *dims = crop_group->state.dims;
285         bNode *node = mpr_prop->custom_func.user_data;
286         NodeTwoXYs *nxy = node->storage;
287         bool is_relative = (bool)node->custom2;
288         rctf rct;
289         two_xy_to_rect(nxy, &rct, dims, is_relative);
290         BLI_rctf_resize(&rct, matrix[0][0], matrix[1][1]);
291         BLI_rctf_recenter(&rct, (matrix[3][0] / dims[0]) + 0.5f, (matrix[3][1] / dims[1]) + 0.5f);
292         BLI_rctf_isect(&(rctf){.xmin = 0, .ymin = 0, .xmax = 1, .ymax = 1}, &rct, &rct);
293         two_xy_from_rect(nxy, &rct, dims, is_relative);
294         manipulator_node_crop_update(crop_group);
295 }
296
297 static bool WIDGETGROUP_node_crop_poll(const bContext *C, wmManipulatorGroupType *UNUSED(wgt))
298 {
299         SpaceNode *snode = CTX_wm_space_node(C);
300
301         if ((snode->flag & SNODE_BACKDRAW) == 0) {
302                 return false;
303         }
304
305         if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
306                 bNode *node = nodeGetActive(snode->edittree);
307
308                 if (node && ELEM(node->type, CMP_NODE_CROP)) {
309                         /* ignore 'use_crop_size', we can't usefully edit the crop in this case. */
310                         if ((node->custom1 & (0 << 1)) == 0) {
311                                 return true;
312                         }
313                 }
314         }
315
316         return false;
317 }
318
319 static void WIDGETGROUP_node_crop_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
320 {
321         struct NodeCropWidgetGroup *crop_group = MEM_mallocN(sizeof(struct NodeCropWidgetGroup), __func__);
322
323         crop_group->border = WM_manipulator_new("MANIPULATOR_WT_cage_2d", mgroup, NULL);
324
325         RNA_enum_set(crop_group->border->ptr, "transform",
326                      ED_MANIPULATOR_CAGE2D_XFORM_FLAG_TRANSLATE | ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE);
327
328         mgroup->customdata = crop_group;
329 }
330
331 static void WIDGETGROUP_node_crop_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
332 {
333         ARegion *ar = CTX_wm_region(C);
334         wmManipulator *mpr = mgroup->manipulators.first;
335
336         SpaceNode *snode = CTX_wm_space_node(C);
337
338         node_manipulator_calc_matrix_space(snode, ar, mpr->matrix_space);
339 }
340
341 static void WIDGETGROUP_node_crop_refresh(const bContext *C, wmManipulatorGroup *mgroup)
342 {
343         struct NodeCropWidgetGroup *crop_group = mgroup->customdata;
344         wmManipulator *mpr = crop_group->border;
345
346         void *lock;
347         Image *ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
348         ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
349
350         if (ibuf) {
351                 crop_group->state.dims[0] = (ibuf->x > 0) ? ibuf->x : 64.0f;
352                 crop_group->state.dims[1] = (ibuf->y > 0) ? ibuf->y : 64.0f;
353
354                 RNA_float_set_array(mpr->ptr, "dimensions", crop_group->state.dims);
355                 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_HIDDEN, false);
356
357                 SpaceNode *snode = CTX_wm_space_node(C);
358                 bNode *node = nodeGetActive(snode->edittree);
359
360                 crop_group->update_data.context = (bContext *)C;
361                 RNA_pointer_create((ID *)snode->edittree, &RNA_CompositorNodeCrop, node, &crop_group->update_data.ptr);
362                 crop_group->update_data.prop = RNA_struct_find_property(&crop_group->update_data.ptr, "relative");
363
364                 WM_manipulator_target_property_def_func(
365                         mpr, "matrix",
366                         &(const struct wmManipulatorPropertyFnParams) {
367                             .value_get_fn = manipulator_node_crop_prop_matrix_get,
368                             .value_set_fn = manipulator_node_crop_prop_matrix_set,
369                             .range_get_fn = NULL,
370                             .user_data = node,
371                         });
372         }
373         else {
374                 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_HIDDEN, true);
375         }
376
377         BKE_image_release_ibuf(ima, ibuf, lock);
378 }
379
380 void NODE_WGT_backdrop_crop(wmManipulatorGroupType *wgt)
381 {
382         wgt->name = "Backdrop Crop Widget";
383         wgt->idname = "NODE_WGT_backdrop_crop";
384
385         wgt->flag |= WM_MANIPULATORGROUPTYPE_PERSISTENT;
386
387         wgt->poll = WIDGETGROUP_node_crop_poll;
388         wgt->setup = WIDGETGROUP_node_crop_setup;
389         wgt->draw_prepare = WIDGETGROUP_node_crop_draw_prepare;
390         wgt->refresh = WIDGETGROUP_node_crop_refresh;
391 }
392
393 /** \} */
394
395 /* -------------------------------------------------------------------- */
396
397 /** \name Sun Beams
398  * \{ */
399
400 struct NodeSunBeamsWidgetGroup {
401         wmManipulator *manipulator;
402
403         struct {
404                 float dims[2];
405         } state;
406 };
407
408 static bool WIDGETGROUP_node_sbeam_poll(const bContext *C, wmManipulatorGroupType *UNUSED(wgt))
409 {
410         SpaceNode *snode = CTX_wm_space_node(C);
411
412         if ((snode->flag & SNODE_BACKDRAW) == 0) {
413                 return false;
414         }
415
416         if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
417                 bNode *node = nodeGetActive(snode->edittree);
418
419                 if (node && ELEM(node->type, CMP_NODE_SUNBEAMS)) {
420                         return true;
421                 }
422         }
423
424         return false;
425 }
426
427 static void WIDGETGROUP_node_sbeam_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
428 {
429         struct NodeSunBeamsWidgetGroup *sbeam_group = MEM_mallocN(sizeof(struct NodeSunBeamsWidgetGroup), __func__);
430
431         sbeam_group->manipulator = WM_manipulator_new("MANIPULATOR_WT_grab_3d", mgroup, NULL);
432         wmManipulator *mpr = sbeam_group->manipulator;
433
434         RNA_enum_set(mpr->ptr, "draw_style",  ED_MANIPULATOR_GRAB_STYLE_CROSS_2D);
435
436         mpr->scale_basis = 0.05f;
437
438         mgroup->customdata = sbeam_group;
439 }
440
441 static void WIDGETGROUP_node_sbeam_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
442 {
443         struct NodeSunBeamsWidgetGroup *sbeam_group = mgroup->customdata;
444         ARegion *ar = CTX_wm_region(C);
445         wmManipulator *mpr = mgroup->manipulators.first;
446
447         SpaceNode *snode = CTX_wm_space_node(C);
448
449         node_manipulator_calc_matrix_space_with_image_dims(snode, ar, sbeam_group->state.dims, mpr->matrix_space);
450 }
451
452 static void WIDGETGROUP_node_sbeam_refresh(const bContext *C, wmManipulatorGroup *mgroup)
453 {
454         struct NodeSunBeamsWidgetGroup *sbeam_group = mgroup->customdata;
455         wmManipulator *mpr = sbeam_group->manipulator;
456
457         void *lock;
458         Image *ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
459         ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
460
461         if (ibuf) {
462                 sbeam_group->state.dims[0] = (ibuf->x > 0) ? ibuf->x : 64.0f;
463                 sbeam_group->state.dims[1] = (ibuf->y > 0) ? ibuf->y : 64.0f;
464
465                 SpaceNode *snode = CTX_wm_space_node(C);
466                 bNode *node = nodeGetActive(snode->edittree);
467
468                 /* need to set property here for undo. TODO would prefer to do this in _init */
469                 PointerRNA nodeptr;
470                 RNA_pointer_create((ID *)snode->edittree, &RNA_CompositorNodeSunBeams, node, &nodeptr);
471                 WM_manipulator_target_property_def_rna(mpr, "offset", &nodeptr, "source", -1);
472
473                 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_DRAW_MODAL, true);
474         }
475         else {
476                 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_HIDDEN, true);
477         }
478
479         BKE_image_release_ibuf(ima, ibuf, lock);
480 }
481
482 void NODE_WGT_backdrop_sun_beams(wmManipulatorGroupType *wgt)
483 {
484         wgt->name = "Sun Beams Widget";
485         wgt->idname = "NODE_WGT_sbeam";
486
487         wgt->flag |= WM_MANIPULATORGROUPTYPE_PERSISTENT;
488
489         wgt->poll = WIDGETGROUP_node_sbeam_poll;
490         wgt->setup = WIDGETGROUP_node_sbeam_setup;
491         wgt->draw_prepare = WIDGETGROUP_node_sbeam_draw_prepare;
492         wgt->refresh = WIDGETGROUP_node_sbeam_refresh;
493 }
494
495 /** \} */
496
497
498
499 /* -------------------------------------------------------------------- */
500
501 /** \name Corner Pin
502  * \{ */
503
504 struct NodeCornerPinWidgetGroup {
505         wmManipulator *manipulators[4];
506
507         struct {
508                 float dims[2];
509         } state;
510 };
511
512 static bool WIDGETGROUP_node_corner_pin_poll(const bContext *C, wmManipulatorGroupType *UNUSED(wgt))
513 {
514         SpaceNode *snode = CTX_wm_space_node(C);
515
516         if ((snode->flag & SNODE_BACKDRAW) == 0) {
517                 return false;
518         }
519
520         if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
521                 bNode *node = nodeGetActive(snode->edittree);
522
523                 if (node && ELEM(node->type, CMP_NODE_CORNERPIN)) {
524                         return true;
525                 }
526         }
527
528         return false;
529 }
530
531 static void WIDGETGROUP_node_corner_pin_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
532 {
533         struct NodeCornerPinWidgetGroup *cpin_group = MEM_mallocN(sizeof(struct NodeCornerPinWidgetGroup), __func__);
534         const wmManipulatorType *wt_grab_3d = WM_manipulatortype_find("MANIPULATOR_WT_grab_3d", false);
535
536         for (int i = 0; i < 4; i++) {
537                 cpin_group->manipulators[i] = WM_manipulator_new_ptr(wt_grab_3d, mgroup, NULL);
538                 wmManipulator *mpr = cpin_group->manipulators[i];
539
540                 RNA_enum_set(mpr->ptr, "draw_style",  ED_MANIPULATOR_GRAB_STYLE_CROSS_2D);
541
542                 mpr->scale_basis = 0.01f;
543         }
544
545         mgroup->customdata = cpin_group;
546 }
547
548 static void WIDGETGROUP_node_corner_pin_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
549 {
550         struct NodeCornerPinWidgetGroup *cpin_group = mgroup->customdata;
551         ARegion *ar = CTX_wm_region(C);
552
553         SpaceNode *snode = CTX_wm_space_node(C);
554
555         float matrix_space[4][4];
556         node_manipulator_calc_matrix_space_with_image_dims(snode, ar, cpin_group->state.dims, matrix_space);
557
558         for (int i = 0; i < 4; i++) {
559                 wmManipulator *mpr = cpin_group->manipulators[i];
560                 copy_m4_m4(mpr->matrix_space, matrix_space);
561         }
562 }
563
564 static void WIDGETGROUP_node_corner_pin_refresh(const bContext *C, wmManipulatorGroup *mgroup)
565 {
566         struct NodeCornerPinWidgetGroup *cpin_group = mgroup->customdata;
567
568         void *lock;
569         Image *ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
570         ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
571
572         if (ibuf) {
573                 cpin_group->state.dims[0] = (ibuf->x > 0) ? ibuf->x : 64.0f;
574                 cpin_group->state.dims[1] = (ibuf->y > 0) ? ibuf->y : 64.0f;
575
576                 SpaceNode *snode = CTX_wm_space_node(C);
577                 bNode *node = nodeGetActive(snode->edittree);
578
579                 /* need to set property here for undo. TODO would prefer to do this in _init */
580                 int i = 0;
581                 for (bNodeSocket *sock = node->inputs.first; sock && i < 4; sock = sock->next) {
582                         if (sock->type == SOCK_VECTOR) {
583                                 wmManipulator *mpr = cpin_group->manipulators[i++];
584
585                                 PointerRNA sockptr;
586                                 RNA_pointer_create((ID *)snode->edittree, &RNA_NodeSocket, sock, &sockptr);
587                                 WM_manipulator_target_property_def_rna(mpr, "offset", &sockptr, "default_value", -1);
588
589                                 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_DRAW_MODAL, true);
590                         }
591                 }
592         }
593         else {
594                 for (int i = 0; i < 4; i++) {
595                         wmManipulator *mpr = cpin_group->manipulators[i];
596                         WM_manipulator_set_flag(mpr, WM_MANIPULATOR_HIDDEN, true);
597                 }
598         }
599
600         BKE_image_release_ibuf(ima, ibuf, lock);
601 }
602
603 void NODE_WGT_backdrop_corner_pin(wmManipulatorGroupType *wgt)
604 {
605         wgt->name = "Corner Pin Widget";
606         wgt->idname = "NODE_WGT_backdrop_corner_pin";
607
608         wgt->flag |= WM_MANIPULATORGROUPTYPE_PERSISTENT;
609
610         wgt->poll = WIDGETGROUP_node_corner_pin_poll;
611         wgt->setup = WIDGETGROUP_node_corner_pin_setup;
612         wgt->draw_prepare = WIDGETGROUP_node_corner_pin_draw_prepare;
613         wgt->refresh = WIDGETGROUP_node_corner_pin_refresh;
614 }
615
616 /** \} */