eb20f4e7175cdda6ef12dac5ae60a2d9b33aa25d
[blender.git] / source / blender / editors / space_node / node_edit.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  * The Original Code is Copyright (C) 2005 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spnode
22  */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "DNA_light_types.h"
27 #include "DNA_material_types.h"
28 #include "DNA_node_types.h"
29 #include "DNA_text_types.h"
30 #include "DNA_world_types.h"
31
32 #include "BLI_math.h"
33 #include "BLI_blenlib.h"
34
35 #include "BKE_context.h"
36 #include "BKE_global.h"
37 #include "BKE_image.h"
38 #include "BKE_library.h"
39 #include "BKE_main.h"
40 #include "BKE_node.h"
41 #include "BKE_report.h"
42 #include "BKE_scene.h"
43
44 #include "DEG_depsgraph.h"
45
46 #include "RE_engine.h"
47 #include "RE_pipeline.h"
48
49
50 #include "ED_node.h"  /* own include */
51 #include "ED_select_utils.h"
52 #include "ED_screen.h"
53 #include "ED_render.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57 #include "RNA_enum_types.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "UI_view2d.h"
63
64 #include "GPU_material.h"
65
66 #include "IMB_imbuf_types.h"
67
68 #include "node_intern.h"  /* own include */
69 #include "NOD_composite.h"
70 #include "NOD_shader.h"
71 #include "NOD_texture.h"
72
73
74 #define USE_ESC_COMPO
75
76 /* ***************** composite job manager ********************** */
77
78 enum {
79         COM_RECALC_COMPOSITE = 1,
80         COM_RECALC_VIEWER    = 2,
81 };
82
83 typedef struct CompoJob {
84         Main *bmain;
85         Scene *scene;
86         bNodeTree *ntree;
87         bNodeTree *localtree;
88         const short *stop;
89         short *do_update;
90         float *progress;
91         int recalc_flags;
92 } CompoJob;
93
94 static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags)
95 {
96         bNode *node;
97
98         for (node = nodetree->nodes.first; node; node = node->next) {
99                 if (node->type == CMP_NODE_COMPOSITE) {
100                         if (recalc_flags & COM_RECALC_COMPOSITE)
101                                 node->flag |= NODE_DO_OUTPUT_RECALC;
102                 }
103                 else if (node->type == CMP_NODE_VIEWER || node->type == CMP_NODE_SPLITVIEWER) {
104                         if (recalc_flags & COM_RECALC_VIEWER)
105                                 node->flag |= NODE_DO_OUTPUT_RECALC;
106                 }
107                 else if (node->type == NODE_GROUP) {
108                         if (node->id)
109                                 compo_tag_output_nodes((bNodeTree *)node->id, recalc_flags);
110                 }
111         }
112 }
113
114 static int compo_get_recalc_flags(const bContext *C)
115 {
116         wmWindowManager *wm = CTX_wm_manager(C);
117         wmWindow *win;
118         int recalc_flags = 0;
119
120         for (win = wm->windows.first; win; win = win->next) {
121                 const bScreen *sc = WM_window_get_active_screen(win);
122                 ScrArea *sa;
123
124                 for (sa = sc->areabase.first; sa; sa = sa->next) {
125                         if (sa->spacetype == SPACE_IMAGE) {
126                                 SpaceImage *sima = sa->spacedata.first;
127                                 if (sima->image) {
128                                         if (sima->image->type == IMA_TYPE_R_RESULT)
129                                                 recalc_flags |= COM_RECALC_COMPOSITE;
130                                         else if (sima->image->type == IMA_TYPE_COMPOSITE)
131                                                 recalc_flags |= COM_RECALC_VIEWER;
132                                 }
133                         }
134                         else if (sa->spacetype == SPACE_NODE) {
135                                 SpaceNode *snode = sa->spacedata.first;
136                                 if (snode->flag & SNODE_BACKDRAW)
137                                         recalc_flags |= COM_RECALC_VIEWER;
138                         }
139                 }
140         }
141
142         return recalc_flags;
143 }
144
145 /* called by compo, only to check job 'stop' value */
146 static int compo_breakjob(void *cjv)
147 {
148         CompoJob *cj = cjv;
149
150         /* without G.is_break 'ESC' wont quit - which annoys users */
151         return (*(cj->stop)
152 #ifdef USE_ESC_COMPO
153                 ||
154                 G.is_break
155 #endif
156                 );
157 }
158
159 /* called by compo, wmJob sends notifier */
160 static void compo_statsdrawjob(void *cjv, const char *UNUSED(str))
161 {
162         CompoJob *cj = cjv;
163
164         *(cj->do_update) = true;
165 }
166
167 /* called by compo, wmJob sends notifier */
168 static void compo_redrawjob(void *cjv)
169 {
170         CompoJob *cj = cjv;
171
172         *(cj->do_update) = true;
173 }
174
175 static void compo_freejob(void *cjv)
176 {
177         CompoJob *cj = cjv;
178
179         if (cj->localtree) {
180                 ntreeLocalMerge(cj->bmain, cj->localtree, cj->ntree);
181         }
182         MEM_freeN(cj);
183 }
184
185 /* only now we copy the nodetree, so adding many jobs while
186  * sliding buttons doesn't frustrate */
187 static void compo_initjob(void *cjv)
188 {
189         CompoJob *cj = cjv;
190
191         cj->localtree = ntreeLocalize(cj->ntree);
192
193         if (cj->recalc_flags)
194                 compo_tag_output_nodes(cj->localtree, cj->recalc_flags);
195 }
196
197 /* called before redraw notifiers, it moves finished previews over */
198 static void compo_updatejob(void *UNUSED(cjv))
199 {
200         WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, NULL);
201 }
202
203 static void compo_progressjob(void *cjv, float progress)
204 {
205         CompoJob *cj = cjv;
206
207         *(cj->progress) = progress;
208 }
209
210 /* only this runs inside thread */
211 static void compo_startjob(void *cjv, short *stop, short *do_update, float *progress)
212 {
213         CompoJob *cj = cjv;
214         bNodeTree *ntree = cj->localtree;
215         Scene *scene = cj->scene;
216         SceneRenderView *srv;
217
218         if (scene->use_nodes == false)
219                 return;
220
221         cj->stop = stop;
222         cj->do_update = do_update;
223         cj->progress = progress;
224
225         ntree->test_break = compo_breakjob;
226         ntree->tbh = cj;
227         ntree->stats_draw = compo_statsdrawjob;
228         ntree->sdh = cj;
229         ntree->progress = compo_progressjob;
230         ntree->prh = cj;
231         ntree->update_draw = compo_redrawjob;
232         ntree->udh = cj;
233
234         // XXX BIF_store_spare();
235         /* 1 is do_previews */
236
237         if ((cj->scene->r.scemode & R_MULTIVIEW) == 0) {
238                 ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings, "");
239         }
240         else {
241                 for (srv = scene->r.views.first; srv; srv = srv->next) {
242                         if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) continue;
243                         ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings, srv->name);
244                 }
245         }
246
247         ntree->test_break = NULL;
248         ntree->stats_draw = NULL;
249         ntree->progress = NULL;
250
251 }
252
253 /**
254  * \param scene_owner: is the owner of the job,
255  * we don't use it for anything else currently so could also be a void pointer,
256  * but for now keep it an 'Scene' for consistency.
257  *
258  * \note only call from spaces `refresh` callbacks, not direct! - use with care.
259  */
260 void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner)
261 {
262         wmJob *wm_job;
263         CompoJob *cj;
264         Main *bmain = CTX_data_main(C);
265         Scene *scene = CTX_data_scene(C);
266
267         /* to fix bug: [#32272] */
268         if (G.is_rendering) {
269                 return;
270         }
271
272 #ifdef USE_ESC_COMPO
273         G.is_break = false;
274 #endif
275
276         BKE_image_backup_render(scene, BKE_image_verify_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"), false);
277
278         wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene_owner, "Compositing",
279                              WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS, WM_JOB_TYPE_COMPOSITE);
280         cj = MEM_callocN(sizeof(CompoJob), "compo job");
281
282         /* customdata for preview thread */
283         cj->bmain = bmain;
284         cj->scene = scene;
285         cj->ntree = nodetree;
286         cj->recalc_flags = compo_get_recalc_flags(C);
287
288         /* setup job */
289         WM_jobs_customdata_set(wm_job, cj, compo_freejob);
290         WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_COMPO_RESULT, NC_SCENE | ND_COMPO_RESULT);
291         WM_jobs_callbacks(wm_job, compo_startjob, compo_initjob, compo_updatejob, NULL);
292
293         WM_jobs_start(CTX_wm_manager(C), wm_job);
294 }
295
296 /* ***************************************** */
297
298 /* operator poll callback */
299 bool composite_node_active(bContext *C)
300 {
301         if (ED_operator_node_active(C)) {
302                 SpaceNode *snode = CTX_wm_space_node(C);
303                 if (ED_node_is_compositor(snode))
304                         return 1;
305         }
306         return 0;
307 }
308
309 /* operator poll callback */
310 bool composite_node_editable(bContext *C)
311 {
312         if (ED_operator_node_editable(C)) {
313                 SpaceNode *snode = CTX_wm_space_node(C);
314                 if (ED_node_is_compositor(snode))
315                         return 1;
316         }
317         return 0;
318 }
319
320 void snode_dag_update(bContext *C, SpaceNode *snode)
321 {
322         Main *bmain = CTX_data_main(C);
323
324         /* for groups, update all ID's using this */
325         if (snode->edittree != snode->nodetree) {
326                 FOREACH_NODETREE_BEGIN(bmain, tntree, id) {
327                         if (ntreeHasTree(tntree, snode->edittree))
328                                 DEG_id_tag_update(id, 0);
329                 } FOREACH_NODETREE_END;
330         }
331
332         DEG_id_tag_update(snode->id, 0);
333 }
334
335 void snode_notify(bContext *C, SpaceNode *snode)
336 {
337         ID *id = snode->id;
338
339         WM_event_add_notifier(C, NC_NODE | NA_EDITED, NULL);
340
341         if (ED_node_is_shader(snode)) {
342                 if (GS(id->name) == ID_MA)
343                         WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id);
344                 else if (GS(id->name) == ID_LA)
345                         WM_main_add_notifier(NC_LAMP | ND_LIGHTING, id);
346                 else if (GS(id->name) == ID_WO)
347                         WM_main_add_notifier(NC_WORLD | ND_WORLD, id);
348         }
349         else if (ED_node_is_compositor(snode))
350                 WM_event_add_notifier(C, NC_SCENE | ND_NODES, id);
351         else if (ED_node_is_texture(snode))
352                 WM_event_add_notifier(C, NC_TEXTURE | ND_NODES, id);
353 }
354
355 void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo)
356 {
357         if (typeinfo)
358                 BLI_strncpy(snode->tree_idname, typeinfo->idname, sizeof(snode->tree_idname));
359         else
360                 snode->tree_idname[0] = '\0';
361 }
362
363 bool ED_node_is_compositor(struct SpaceNode *snode)
364 {
365         return STREQ(snode->tree_idname, ntreeType_Composite->idname);
366 }
367
368 bool ED_node_is_shader(struct SpaceNode *snode)
369 {
370         return STREQ(snode->tree_idname, ntreeType_Shader->idname);
371 }
372
373 bool ED_node_is_texture(struct SpaceNode *snode)
374 {
375         return STREQ(snode->tree_idname, ntreeType_Texture->idname);
376 }
377
378 /* assumes nothing being done in ntree yet, sets the default in/out node */
379 /* called from shading buttons or header */
380 void ED_node_shader_default(const bContext *C, ID *id)
381 {
382         bNode *in, *out;
383         bNodeSocket *fromsock, *tosock, *sock;
384         bNodeTree *ntree;
385         int output_type, shader_type;
386         float color[4] = { 0.0f, 0.0f, 0.0f, 1.0f }, strength = 1.0f;
387
388         ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
389
390         switch (GS(id->name)) {
391                 case ID_MA:
392                 {
393                         Material *ma = (Material *)id;
394                         ma->nodetree = ntree;
395
396                         output_type = SH_NODE_OUTPUT_MATERIAL;
397                         shader_type = SH_NODE_BSDF_PRINCIPLED;
398
399                         copy_v3_v3(color, &ma->r);
400                         strength = 0.0f;
401                         break;
402                 }
403                 case ID_WO:
404                 {
405                         World *wo = (World *)id;
406                         wo->nodetree = ntree;
407
408                         output_type = SH_NODE_OUTPUT_WORLD;
409                         shader_type = SH_NODE_BACKGROUND;
410
411                         copy_v3_v3(color, &wo->horr);
412                         strength = 1.0f;
413                         break;
414                 }
415                 case ID_LA:
416                 {
417                         Light *la = (Light *)id;
418                         la->nodetree = ntree;
419
420                         output_type = SH_NODE_OUTPUT_LIGHT;
421                         shader_type = SH_NODE_EMISSION;
422
423                         copy_v3_v3(color, &la->r);
424                         if (la->type == LA_LOCAL || la->type == LA_SPOT || la->type == LA_AREA)
425                                 strength = 100.0f;
426                         else
427                                 strength = 1.0f;
428                         break;
429                 }
430                 default:
431                         printf("ED_node_shader_default called on wrong ID type.\n");
432                         return;
433         }
434
435         out = nodeAddStaticNode(C, ntree, output_type);
436         out->locx = 300.0f; out->locy = 300.0f;
437
438         in = nodeAddStaticNode(C, ntree, shader_type);
439         in->locx = 10.0f; in->locy = 300.0f;
440         nodeSetActive(ntree, in);
441
442         /* only a link from color to color */
443         fromsock = in->outputs.first;
444         tosock = out->inputs.first;
445         nodeAddLink(ntree, in, fromsock, out, tosock);
446
447         /* default values */
448         PointerRNA sockptr;
449         sock = in->inputs.first;
450         RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &sockptr);
451
452         RNA_float_set_array(&sockptr, "default_value", color);
453
454         if (strength != 0.0f) {
455                 sock = in->inputs.last;
456                 RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &sockptr);
457                 RNA_float_set(&sockptr, "default_value", strength);
458         }
459
460         ntreeUpdateTree(CTX_data_main(C), ntree);
461 }
462
463 /* assumes nothing being done in ntree yet, sets the default in/out node */
464 /* called from shading buttons or header */
465 void ED_node_composit_default(const bContext *C, struct Scene *sce)
466 {
467         bNode *in, *out;
468         bNodeSocket *fromsock, *tosock;
469
470         /* but lets check it anyway */
471         if (sce->nodetree) {
472                 if (G.debug & G_DEBUG)
473                         printf("error in composite initialize\n");
474                 return;
475         }
476
477         sce->nodetree = ntreeAddTree(NULL, "Compositing Nodetree", ntreeType_Composite->idname);
478
479         sce->nodetree->chunksize = 256;
480         sce->nodetree->edit_quality = NTREE_QUALITY_HIGH;
481         sce->nodetree->render_quality = NTREE_QUALITY_HIGH;
482
483         out = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_COMPOSITE);
484         out->locx = 300.0f; out->locy = 400.0f;
485
486         in = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_R_LAYERS);
487         in->locx = 10.0f; in->locy = 400.0f;
488         nodeSetActive(sce->nodetree, in);
489
490         /* links from color to color */
491         fromsock = in->outputs.first;
492         tosock = out->inputs.first;
493         nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
494
495         ntreeUpdateTree(CTX_data_main(C), sce->nodetree);
496 }
497
498 /* assumes nothing being done in ntree yet, sets the default in/out node */
499 /* called from shading buttons or header */
500 void ED_node_texture_default(const bContext *C, Tex *tx)
501 {
502         bNode *in, *out;
503         bNodeSocket *fromsock, *tosock;
504
505         /* but lets check it anyway */
506         if (tx->nodetree) {
507                 if (G.debug & G_DEBUG)
508                         printf("error in texture initialize\n");
509                 return;
510         }
511
512         tx->nodetree = ntreeAddTree(NULL, "Texture Nodetree", ntreeType_Texture->idname);
513
514         out = nodeAddStaticNode(C, tx->nodetree, TEX_NODE_OUTPUT);
515         out->locx = 300.0f; out->locy = 300.0f;
516
517         in = nodeAddStaticNode(C, tx->nodetree, TEX_NODE_CHECKER);
518         in->locx = 10.0f; in->locy = 300.0f;
519         nodeSetActive(tx->nodetree, in);
520
521         fromsock = in->outputs.first;
522         tosock = out->inputs.first;
523         nodeAddLink(tx->nodetree, in, fromsock, out, tosock);
524
525         ntreeUpdateTree(CTX_data_main(C), tx->nodetree);
526 }
527
528 /* Here we set the active tree(s), even called for each redraw now, so keep it fast :) */
529 void snode_set_context(const bContext *C)
530 {
531         SpaceNode *snode = CTX_wm_space_node(C);
532         bNodeTreeType *treetype = ntreeTypeFind(snode->tree_idname);
533         bNodeTree *ntree = snode->nodetree;
534         ID *id = snode->id, *from = snode->from;
535
536         /* check the tree type */
537         if (!treetype ||
538             (treetype->poll && !treetype->poll(C, treetype)))
539         {
540                 /* invalid tree type, skip
541                  * NB: not resetting the node path here, invalid bNodeTreeType
542                  * may still be registered at a later point.
543                  */
544                 return;
545         }
546
547         if (snode->nodetree && !STREQ(snode->nodetree->idname, snode->tree_idname)) {
548                 /* current tree does not match selected type, clear tree path */
549                 ntree = NULL;
550                 id = NULL;
551                 from = NULL;
552         }
553
554         if (!(snode->flag & SNODE_PIN) || ntree == NULL) {
555                 if (treetype->get_from_context) {
556                         /* reset and update from context */
557                         ntree = NULL;
558                         id = NULL;
559                         from = NULL;
560
561                         treetype->get_from_context(C, treetype, &ntree, &id, &from);
562                 }
563         }
564
565         if (snode->nodetree != ntree || snode->id != id || snode->from != from ||
566             (snode->treepath.last == NULL && ntree))
567         {
568                 ED_node_tree_start(snode, ntree, id, from);
569         }
570 }
571
572 void snode_update(SpaceNode *snode, bNode *node)
573 {
574         bNodeTreePath *path;
575
576         /* XXX this only updates nodes in the current node space tree path.
577          * The function supposedly should update any potential group node linking to changed tree,
578          * this really requires a working depsgraph ...
579          */
580
581         /* update all edited group nodes */
582         path = snode->treepath.last;
583         if (path) {
584                 bNodeTree *ngroup = path->nodetree;
585                 for (path = path->prev; path; path = path->prev) {
586                         nodeUpdateID(path->nodetree, (ID *)ngroup);
587                         ngroup = path->nodetree;
588                 }
589         }
590
591         if (node)
592                 nodeUpdate(snode->edittree, node);
593 }
594
595 void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node)
596 {
597         const bool was_active_texture = (node->flag & NODE_ACTIVE_TEXTURE) != 0;
598
599         nodeSetActive(ntree, node);
600
601         if (node->type != NODE_GROUP) {
602                 const bool was_output = (node->flag & NODE_DO_OUTPUT) != 0;
603                 bool do_update = false;
604
605                 /* generic node group output: set node as active output */
606                 if (node->type == NODE_GROUP_OUTPUT) {
607                         bNode *tnode;
608                         for (tnode = ntree->nodes.first; tnode; tnode = tnode->next)
609                                 if (tnode->type == NODE_GROUP_OUTPUT)
610                                         tnode->flag &= ~NODE_DO_OUTPUT;
611
612                         node->flag |= NODE_DO_OUTPUT;
613                         if (!was_output)
614                                 do_update = 1;
615                 }
616
617                 /* tree specific activate calls */
618                 if (ntree->type == NTREE_SHADER) {
619                         /* when we select a material, active texture is cleared, for buttons */
620                         if (node->id && ELEM(GS(node->id->name), ID_MA, ID_LA, ID_WO))
621                                 nodeClearActiveID(ntree, ID_TE);
622
623                         if (ELEM(node->type, SH_NODE_OUTPUT_MATERIAL,
624                                  SH_NODE_OUTPUT_WORLD, SH_NODE_OUTPUT_LIGHT, SH_NODE_OUTPUT_LINESTYLE))
625                         {
626                                 bNode *tnode;
627
628                                 for (tnode = ntree->nodes.first; tnode; tnode = tnode->next)
629                                         if (tnode->type == node->type)
630                                                 tnode->flag &= ~NODE_DO_OUTPUT;
631
632                                 node->flag |= NODE_DO_OUTPUT;
633                                 if (was_output == 0)
634                                         ED_node_tag_update_nodetree(bmain, ntree, node);
635                         }
636                         else if (do_update)
637                                 ED_node_tag_update_nodetree(bmain, ntree, node);
638
639                         /* if active texture changed, free glsl materials */
640                         if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) {
641                                 Material *ma;
642                                 World *wo;
643
644                                 for (ma = bmain->materials.first; ma; ma = ma->id.next)
645                                         if (ma->nodetree && ma->use_nodes && ntreeHasTree(ma->nodetree, ntree))
646                                                 GPU_material_free(&ma->gpumaterial);
647
648                                 for (wo = bmain->worlds.first; wo; wo = wo->id.next)
649                                         if (wo->nodetree && wo->use_nodes && ntreeHasTree(wo->nodetree, ntree))
650                                                 GPU_material_free(&wo->gpumaterial);
651
652                                 ED_node_tag_update_nodetree(bmain, ntree, node);
653                                 WM_main_add_notifier(NC_IMAGE, NULL);
654                         }
655
656                         WM_main_add_notifier(NC_MATERIAL | ND_NODES, node->id);
657                 }
658                 else if (ntree->type == NTREE_COMPOSIT) {
659                         /* make active viewer, currently only 1 supported... */
660                         if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
661                                 bNode *tnode;
662
663
664                                 for (tnode = ntree->nodes.first; tnode; tnode = tnode->next)
665                                         if (ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
666                                                 tnode->flag &= ~NODE_DO_OUTPUT;
667
668                                 node->flag |= NODE_DO_OUTPUT;
669                                 if (was_output == 0)
670                                         ED_node_tag_update_nodetree(bmain, ntree, node);
671
672                                 /* addnode() doesn't link this yet... */
673                                 node->id = (ID *)BKE_image_verify_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
674                         }
675                         else if (node->type == CMP_NODE_COMPOSITE) {
676                                 if (was_output == 0) {
677                                         bNode *tnode;
678
679                                         for (tnode = ntree->nodes.first; tnode; tnode = tnode->next)
680                                                 if (tnode->type == CMP_NODE_COMPOSITE)
681                                                         tnode->flag &= ~NODE_DO_OUTPUT;
682
683                                         node->flag |= NODE_DO_OUTPUT;
684                                         ED_node_tag_update_nodetree(bmain, ntree, node);
685                                 }
686                         }
687                         else if (do_update)
688                                 ED_node_tag_update_nodetree(bmain, ntree, node);
689                 }
690                 else if (ntree->type == NTREE_TEXTURE) {
691                         // XXX
692 #if 0
693                         if (node->id)
694                                 ;  // XXX BIF_preview_changed(-1);
695                         // allqueue(REDRAWBUTSSHADING, 1);
696                         // allqueue(REDRAWIPO, 0);
697 #endif
698                 }
699         }
700 }
701
702 void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree))
703 {
704         /* XXX This does not work due to layout functions relying on node->block,
705          * which only exists during actual drawing. Can we rely on valid totr rects?
706          */
707         /* make sure nodes have correct bounding boxes after transform */
708         /* node_update_nodetree(C, ntree, 0.0f, 0.0f); */
709 }
710
711 /* ***************** generic operator functions for nodes ***************** */
712
713 #if 0 /* UNUSED */
714
715 static bool edit_node_poll(bContext *C)
716 {
717         return ED_operator_node_active(C);
718 }
719
720 static void edit_node_properties(wmOperatorType *ot)
721 {
722         /* XXX could node be a context pointer? */
723         RNA_def_string(ot->srna, "node", NULL, MAX_NAME, "Node", "");
724         RNA_def_int(ot->srna, "socket", 0, 0, MAX_SOCKET, "Socket", "", 0, MAX_SOCKET);
725         RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Side", "");
726 }
727
728 static int edit_node_invoke_properties(bContext *C, wmOperator *op)
729 {
730         if (!RNA_struct_property_is_set(op->ptr, "node")) {
731                 bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_Node).data;
732                 if (!node)
733                         return 0;
734                 else
735                         RNA_string_set(op->ptr, "node", node->name);
736         }
737
738         if (!RNA_struct_property_is_set(op->ptr, "in_out"))
739                 RNA_enum_set(op->ptr, "in_out", SOCK_IN);
740
741         if (!RNA_struct_property_is_set(op->ptr, "socket"))
742                 RNA_int_set(op->ptr, "socket", 0);
743
744         return 1;
745 }
746
747 static void edit_node_properties_get(wmOperator *op, bNodeTree *ntree, bNode **rnode, bNodeSocket **rsock, int *rin_out)
748 {
749         bNode *node;
750         bNodeSocket *sock = NULL;
751         char nodename[MAX_NAME];
752         int sockindex;
753         int in_out;
754
755         RNA_string_get(op->ptr, "node", nodename);
756         node = nodeFindNodebyName(ntree, nodename);
757
758         in_out = RNA_enum_get(op->ptr, "in_out");
759
760         sockindex = RNA_int_get(op->ptr, "socket");
761         switch (in_out) {
762                 case SOCK_IN:   sock = BLI_findlink(&node->inputs, sockindex);  break;
763                 case SOCK_OUT:  sock = BLI_findlink(&node->outputs, sockindex); break;
764         }
765
766         if (rnode)
767                 *rnode = node;
768         if (rsock)
769                 *rsock = sock;
770         if (rin_out)
771                 *rin_out = in_out;
772 }
773 #endif
774
775 /* ************************** Node generic ************** */
776
777 /* is rct in visible part of node? */
778 static bNode *visible_node(SpaceNode *snode, const rctf *rct)
779 {
780         bNode *node;
781
782         for (node = snode->edittree->nodes.last; node; node = node->prev) {
783                 if (BLI_rctf_isect(&node->totr, rct, NULL))
784                         break;
785         }
786         return node;
787 }
788
789 /* ********************** size widget operator ******************** */
790
791 typedef struct NodeSizeWidget {
792         float mxstart, mystart;
793         float oldlocx, oldlocy;
794         float oldoffsetx, oldoffsety;
795         float oldwidth, oldheight;
796         float oldminiwidth;
797         int directions;
798 } NodeSizeWidget;
799
800 static void node_resize_init(bContext *C, wmOperator *op, const wmEvent *UNUSED(event), bNode *node, int dir)
801 {
802         SpaceNode *snode = CTX_wm_space_node(C);
803
804         NodeSizeWidget *nsw = MEM_callocN(sizeof(NodeSizeWidget), "size widget op data");
805
806         op->customdata = nsw;
807         nsw->mxstart = snode->cursor[0];
808         nsw->mystart = snode->cursor[1];
809
810         /* store old */
811         nsw->oldlocx = node->locx;
812         nsw->oldlocy = node->locy;
813         nsw->oldoffsetx = node->offsetx;
814         nsw->oldoffsety = node->offsety;
815         nsw->oldwidth = node->width;
816         nsw->oldheight = node->height;
817         nsw->oldminiwidth = node->miniwidth;
818         nsw->directions = dir;
819
820         WM_cursor_modal_set(CTX_wm_window(C), node_get_resize_cursor(dir));
821         /* add modal handler */
822         WM_event_add_modal_handler(C, op);
823 }
824
825 static void node_resize_exit(bContext *C, wmOperator *op, bool UNUSED(cancel))
826 {
827         WM_cursor_modal_restore(CTX_wm_window(C));
828
829         MEM_freeN(op->customdata);
830         op->customdata = NULL;
831 }
832
833 static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
834 {
835         SpaceNode *snode = CTX_wm_space_node(C);
836         ARegion *ar = CTX_wm_region(C);
837         bNode *node = nodeGetActive(snode->edittree);
838         NodeSizeWidget *nsw = op->customdata;
839         float mx, my, dx, dy;
840
841         switch (event->type) {
842                 case MOUSEMOVE:
843
844                         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &mx, &my);
845                         dx = (mx - nsw->mxstart) / UI_DPI_FAC;
846                         dy = (my - nsw->mystart) / UI_DPI_FAC;
847
848                         if (node) {
849                                 /* width can use node->width or node->miniwidth (hidden nodes) */
850                                 float *pwidth;
851                                 float oldwidth, widthmin, widthmax;
852                                 /* ignore hidden flag for frame nodes */
853                                 bool use_hidden = (node->type != NODE_FRAME);
854                                 if (use_hidden && node->flag & NODE_HIDDEN) {
855                                         pwidth = &node->miniwidth;
856                                         oldwidth = nsw->oldminiwidth;
857                                         widthmin = 0.0f;
858                                 }
859                                 else {
860                                         pwidth = &node->width;
861                                         oldwidth = nsw->oldwidth;
862                                         widthmin = node->typeinfo->minwidth;
863                                 }
864                                 widthmax = node->typeinfo->maxwidth;
865
866                                 {
867                                         if (nsw->directions & NODE_RESIZE_RIGHT) {
868                                                 *pwidth = oldwidth + dx;
869                                                 CLAMP(*pwidth, widthmin, widthmax);
870                                         }
871                                         if (nsw->directions & NODE_RESIZE_LEFT) {
872                                                 float locmax = nsw->oldlocx + oldwidth;
873
874                                                 node->locx = nsw->oldlocx + dx;
875                                                 CLAMP(node->locx, locmax - widthmax, locmax - widthmin);
876                                                 *pwidth = locmax - node->locx;
877                                         }
878                                 }
879
880                                 /* height works the other way round ... */
881                                 {
882                                         float heightmin = UI_DPI_FAC * node->typeinfo->minheight;
883                                         float heightmax = UI_DPI_FAC * node->typeinfo->maxheight;
884                                         if (nsw->directions & NODE_RESIZE_TOP) {
885                                                 float locmin = nsw->oldlocy - nsw->oldheight;
886
887                                                 node->locy = nsw->oldlocy + dy;
888                                                 CLAMP(node->locy, locmin + heightmin, locmin + heightmax);
889                                                 node->height = node->locy - locmin;
890                                         }
891                                         if (nsw->directions & NODE_RESIZE_BOTTOM) {
892                                                 node->height = nsw->oldheight - dy;
893                                                 CLAMP(node->height, heightmin, heightmax);
894                                         }
895                                 }
896
897                                 /* XXX make callback? */
898                                 if (node->type == NODE_FRAME) {
899                                         /* keep the offset symmetric around center point */
900                                         if (nsw->directions & NODE_RESIZE_LEFT) {
901                                                 node->locx = nsw->oldlocx + 0.5f * dx;
902                                                 node->offsetx = nsw->oldoffsetx + 0.5f * dx;
903                                         }
904                                         if (nsw->directions & NODE_RESIZE_RIGHT) {
905                                                 node->locx = nsw->oldlocx + 0.5f * dx;
906                                                 node->offsetx = nsw->oldoffsetx - 0.5f * dx;
907                                         }
908                                         if (nsw->directions & NODE_RESIZE_TOP) {
909                                                 node->locy = nsw->oldlocy + 0.5f * dy;
910                                                 node->offsety = nsw->oldoffsety + 0.5f * dy;
911                                         }
912                                         if (nsw->directions & NODE_RESIZE_BOTTOM) {
913                                                 node->locy = nsw->oldlocy + 0.5f * dy;
914                                                 node->offsety = nsw->oldoffsety - 0.5f * dy;
915                                         }
916                                 }
917                         }
918
919                         ED_region_tag_redraw(ar);
920
921                         break;
922
923                 case LEFTMOUSE:
924                 case MIDDLEMOUSE:
925                 case RIGHTMOUSE:
926                         if (event->val == KM_RELEASE) {
927                                 node_resize_exit(C, op, false);
928                                 ED_node_post_apply_transform(C, snode->edittree);
929
930                                 return OPERATOR_FINISHED;
931                         }
932                         break;
933         }
934
935         return OPERATOR_RUNNING_MODAL;
936 }
937
938 static int node_resize_invoke(bContext *C, wmOperator *op, const wmEvent *event)
939 {
940         SpaceNode *snode = CTX_wm_space_node(C);
941         ARegion *ar = CTX_wm_region(C);
942         bNode *node = nodeGetActive(snode->edittree);
943         int dir;
944
945         if (node) {
946                 float cursor[2];
947
948                 /* convert mouse coordinates to v2d space */
949                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1],
950                                          &cursor[0], &cursor[1]);
951                 dir = node->typeinfo->resize_area_func(node, cursor[0], cursor[1]);
952                 if (dir != 0) {
953                         node_resize_init(C, op, event, node, dir);
954                         return OPERATOR_RUNNING_MODAL;
955                 }
956         }
957         return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
958 }
959
960 static void node_resize_cancel(bContext *C, wmOperator *op)
961 {
962         node_resize_exit(C, op, true);
963 }
964
965 void NODE_OT_resize(wmOperatorType *ot)
966 {
967         /* identifiers */
968         ot->name = "Resize Node";
969         ot->idname = "NODE_OT_resize";
970         ot->description = "Resize a node";
971
972         /* api callbacks */
973         ot->invoke = node_resize_invoke;
974         ot->modal = node_resize_modal;
975         ot->poll = ED_operator_node_active;
976         ot->cancel = node_resize_cancel;
977
978         /* flags */
979         ot->flag = OPTYPE_BLOCKING;
980 }
981
982
983 /* ********************** hidden sockets ******************** */
984
985 int node_has_hidden_sockets(bNode *node)
986 {
987         bNodeSocket *sock;
988
989         for (sock = node->inputs.first; sock; sock = sock->next)
990                 if (sock->flag & SOCK_HIDDEN)
991                         return 1;
992         for (sock = node->outputs.first; sock; sock = sock->next)
993                 if (sock->flag & SOCK_HIDDEN)
994                         return 1;
995         return 0;
996 }
997
998 void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
999 {
1000         bNodeSocket *sock;
1001
1002         if (set == 0) {
1003                 for (sock = node->inputs.first; sock; sock = sock->next)
1004                         sock->flag &= ~SOCK_HIDDEN;
1005                 for (sock = node->outputs.first; sock; sock = sock->next)
1006                         sock->flag &= ~SOCK_HIDDEN;
1007         }
1008         else {
1009                 /* hide unused sockets */
1010                 for (sock = node->inputs.first; sock; sock = sock->next) {
1011                         if (sock->link == NULL)
1012                                 sock->flag |= SOCK_HIDDEN;
1013                 }
1014                 for (sock = node->outputs.first; sock; sock = sock->next) {
1015                         if (nodeCountSocketLinks(snode->edittree, sock) == 0)
1016                                 sock->flag |= SOCK_HIDDEN;
1017                 }
1018         }
1019 }
1020
1021
1022 /* checks snode->mouse position, and returns found node/socket */
1023 /* type is SOCK_IN and/or SOCK_OUT */
1024 int node_find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, float cursor[2], int in_out)
1025 {
1026         bNode *node;
1027         bNodeSocket *sock;
1028         rctf rect;
1029
1030         *nodep = NULL;
1031         *sockp = NULL;
1032
1033         /* check if we click in a socket */
1034         for (node = snode->edittree->nodes.first; node; node = node->next) {
1035
1036                 BLI_rctf_init_pt_radius(&rect, cursor, NODE_SOCKSIZE + 4);
1037
1038                 if (!(node->flag & NODE_HIDDEN)) {
1039                         /* extra padding inside and out - allow dragging on the text areas too */
1040                         if (in_out == SOCK_IN) {
1041                                 rect.xmax += NODE_SOCKSIZE;
1042                                 rect.xmin -= NODE_SOCKSIZE * 4;
1043                         }
1044                         else if (in_out == SOCK_OUT) {
1045                                 rect.xmax += NODE_SOCKSIZE * 4;
1046                                 rect.xmin -= NODE_SOCKSIZE;
1047                         }
1048                 }
1049
1050                 if (in_out & SOCK_IN) {
1051                         for (sock = node->inputs.first; sock; sock = sock->next) {
1052                                 if (!nodeSocketIsHidden(sock)) {
1053                                         if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
1054                                                 if (node == visible_node(snode, &rect)) {
1055                                                         *nodep = node;
1056                                                         *sockp = sock;
1057                                                         return 1;
1058                                                 }
1059                                         }
1060                                 }
1061                         }
1062                 }
1063                 if (in_out & SOCK_OUT) {
1064                         for (sock = node->outputs.first; sock; sock = sock->next) {
1065                                 if (!nodeSocketIsHidden(sock)) {
1066                                         if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
1067                                                 if (node == visible_node(snode, &rect)) {
1068                                                         *nodep = node;
1069                                                         *sockp = sock;
1070                                                         return 1;
1071                                                 }
1072                                         }
1073                                 }
1074                         }
1075                 }
1076         }
1077
1078         return 0;
1079 }
1080
1081 /* ****************** Duplicate *********************** */
1082
1083 static void node_duplicate_reparent_recursive(bNode *node)
1084 {
1085         bNode *parent;
1086
1087         node->flag |= NODE_TEST;
1088
1089         /* find first selected parent */
1090         for (parent = node->parent; parent; parent = parent->parent) {
1091                 if (parent->flag & SELECT) {
1092                         if (!(parent->flag & NODE_TEST))
1093                                 node_duplicate_reparent_recursive(parent);
1094                         break;
1095                 }
1096         }
1097         /* reparent node copy to parent copy */
1098         if (parent) {
1099                 nodeDetachNode(node->new_node);
1100                 nodeAttachNode(node->new_node, parent->new_node);
1101         }
1102 }
1103
1104 static int node_duplicate_exec(bContext *C, wmOperator *op)
1105 {
1106         Main *bmain = CTX_data_main(C);
1107         SpaceNode *snode = CTX_wm_space_node(C);
1108         bNodeTree *ntree = snode->edittree;
1109         bNode *node, *newnode, *lastnode;
1110         bNodeLink *link, *newlink, *lastlink;
1111         const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs");
1112         bool do_tag_update = false;
1113
1114         ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
1115
1116         lastnode = ntree->nodes.last;
1117         for (node = ntree->nodes.first; node; node = node->next) {
1118                 if (node->flag & SELECT) {
1119                         newnode = BKE_node_copy_ex(ntree, node, LIB_ID_COPY_DEFAULT);
1120
1121                         /* to ensure redraws or rerenders happen */
1122                         ED_node_tag_update_id(snode->id);
1123                 }
1124
1125                 /* make sure we don't copy new nodes again! */
1126                 if (node == lastnode)
1127                         break;
1128         }
1129
1130         /* copy links between selected nodes
1131          * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy!
1132          */
1133         lastlink = ntree->links.last;
1134         for (link = ntree->links.first; link; link = link->next) {
1135                 /* This creates new links between copied nodes.
1136                  * If keep_inputs is set, also copies input links from unselected (when fromnode==NULL)!
1137                  */
1138                 if (link->tonode && (link->tonode->flag & NODE_SELECT) &&
1139                     (keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT))))
1140                 {
1141                         newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
1142                         newlink->flag = link->flag;
1143                         newlink->tonode = link->tonode->new_node;
1144                         newlink->tosock = link->tosock->new_sock;
1145                         if (link->fromnode && (link->fromnode->flag & NODE_SELECT)) {
1146                                 newlink->fromnode = link->fromnode->new_node;
1147                                 newlink->fromsock = link->fromsock->new_sock;
1148                         }
1149                         else {
1150                                 /* input node not copied, this keeps the original input linked */
1151                                 newlink->fromnode = link->fromnode;
1152                                 newlink->fromsock = link->fromsock;
1153                         }
1154
1155                         BLI_addtail(&ntree->links, newlink);
1156                 }
1157
1158                 /* make sure we don't copy new links again! */
1159                 if (link == lastlink)
1160                         break;
1161         }
1162
1163         /* clear flags for recursive depth-first iteration */
1164         for (node = ntree->nodes.first; node; node = node->next)
1165                 node->flag &= ~NODE_TEST;
1166         /* reparent copied nodes */
1167         for (node = ntree->nodes.first; node; node = node->next) {
1168                 if ((node->flag & SELECT) && !(node->flag & NODE_TEST))
1169                         node_duplicate_reparent_recursive(node);
1170
1171                 /* only has to check old nodes */
1172                 if (node == lastnode)
1173                         break;
1174         }
1175
1176         /* deselect old nodes, select the copies instead */
1177         for (node = ntree->nodes.first; node; node = node->next) {
1178                 if (node->flag & SELECT) {
1179                         /* has been set during copy above */
1180                         newnode = node->new_node;
1181
1182                         nodeSetSelected(node, false);
1183                         node->flag &= ~NODE_ACTIVE;
1184                         nodeSetSelected(newnode, true);
1185
1186                         do_tag_update |= (do_tag_update || node_connected_to_output(bmain, ntree, newnode));
1187                 }
1188
1189                 /* make sure we don't copy new nodes again! */
1190                 if (node == lastnode)
1191                         break;
1192         }
1193
1194         ntreeUpdateTree(CTX_data_main(C), snode->edittree);
1195
1196         snode_notify(C, snode);
1197         if (do_tag_update) {
1198                 snode_dag_update(C, snode);
1199         }
1200
1201         return OPERATOR_FINISHED;
1202 }
1203
1204 void NODE_OT_duplicate(wmOperatorType *ot)
1205 {
1206         /* identifiers */
1207         ot->name = "Duplicate Nodes";
1208         ot->description = "Duplicate selected nodes";
1209         ot->idname = "NODE_OT_duplicate";
1210
1211         /* api callbacks */
1212         ot->exec = node_duplicate_exec;
1213         ot->poll = ED_operator_node_editable;
1214
1215         /* flags */
1216         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1217
1218         RNA_def_boolean(ot->srna, "keep_inputs", 0, "Keep Inputs", "Keep the input links to duplicated nodes");
1219 }
1220
1221 bool ED_node_select_check(ListBase *lb)
1222 {
1223         for (bNode *node = lb->first; node; node = node->next) {
1224                 if (node->flag & NODE_SELECT) {
1225                         return true;
1226                 }
1227         }
1228
1229         return false;
1230 }
1231
1232 void ED_node_select_all(ListBase *lb, int action)
1233 {
1234         if (action == SEL_TOGGLE) {
1235                 if (ED_node_select_check(lb))
1236                         action = SEL_DESELECT;
1237                 else
1238                         action = SEL_SELECT;
1239         }
1240
1241         for (bNode *node = lb->first; node; node = node->next) {
1242                 switch (action) {
1243                         case SEL_SELECT:
1244                                 nodeSetSelected(node, true);
1245                                 break;
1246                         case SEL_DESELECT:
1247                                 nodeSetSelected(node, false);
1248                                 break;
1249                         case SEL_INVERT:
1250                                 nodeSetSelected(node, !(node->flag & SELECT));
1251                                 break;
1252                 }
1253         }
1254 }
1255
1256 /* ******************************** */
1257 // XXX some code needing updating to operators...
1258
1259
1260 /* goes over all scenes, reads render layers */
1261 static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op))
1262 {
1263         Main *bmain = CTX_data_main(C);
1264         SpaceNode *snode = CTX_wm_space_node(C);
1265         Scene *curscene = CTX_data_scene(C), *scene;
1266         bNode *node;
1267
1268         ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
1269
1270         /* first tag scenes unread */
1271         for (scene = bmain->scenes.first; scene; scene = scene->id.next)
1272                 scene->id.tag |= LIB_TAG_DOIT;
1273
1274         for (node = snode->edittree->nodes.first; node; node = node->next) {
1275                 if (node->type == CMP_NODE_R_LAYERS) {
1276                         ID *id = node->id;
1277                         if (id->tag & LIB_TAG_DOIT) {
1278                                 RE_ReadRenderResult(curscene, (Scene *)id);
1279                                 ntreeCompositTagRender((Scene *)id);
1280                                 id->tag &= ~LIB_TAG_DOIT;
1281                         }
1282                 }
1283         }
1284
1285         snode_notify(C, snode);
1286         snode_dag_update(C, snode);
1287
1288         return OPERATOR_FINISHED;
1289 }
1290
1291 void NODE_OT_read_viewlayers(wmOperatorType *ot)
1292 {
1293
1294         ot->name = "Read View Layers";
1295         ot->idname = "NODE_OT_read_viewlayers";
1296         ot->description = "Read all render layers of all used scenes";
1297
1298         ot->exec = node_read_viewlayers_exec;
1299
1300         ot->poll = composite_node_active;
1301
1302         /* flags */
1303         ot->flag = 0;
1304 }
1305
1306 int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
1307 {
1308         Scene *sce = CTX_data_scene(C);
1309         bNode *node;
1310
1311         for (node = sce->nodetree->nodes.first; node; node = node->next) {
1312                 if (node->id == (ID *)sce && node->need_exec) {
1313                         break;
1314                 }
1315         }
1316         if (node) {
1317                 ViewLayer *view_layer = BLI_findlink(&sce->view_layers, node->custom1);
1318
1319                 if (view_layer) {
1320                         PointerRNA op_ptr;
1321
1322                         WM_operator_properties_create(&op_ptr, "RENDER_OT_render");
1323                         RNA_string_set(&op_ptr, "layer", view_layer->name);
1324                         RNA_string_set(&op_ptr, "scene", sce->id.name + 2);
1325
1326                         /* to keep keypositions */
1327                         sce->r.scemode |= R_NO_FRAME_UPDATE;
1328
1329                         WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr);
1330
1331                         WM_operator_properties_free(&op_ptr);
1332
1333                         return OPERATOR_FINISHED;
1334                 }
1335         }
1336         return OPERATOR_CANCELLED;
1337 }
1338
1339 void NODE_OT_render_changed(wmOperatorType *ot)
1340 {
1341         ot->name = "Render Changed Layer";
1342         ot->idname = "NODE_OT_render_changed";
1343         ot->description = "Render current scene, when input node's layer has been changed";
1344
1345         ot->exec = node_render_changed_exec;
1346
1347         ot->poll = composite_node_active;
1348
1349         /* flags */
1350         ot->flag = 0;
1351 }
1352
1353
1354 /* ****************** Hide operator *********************** */
1355
1356 static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
1357 {
1358         bNode *node;
1359         int tot_eq = 0, tot_neq = 0;
1360
1361         /* Toggles the flag on all selected nodes.
1362          * If the flag is set on all nodes it is unset.
1363          * If the flag is not set on all nodes, it is set.
1364          */
1365         for (node = snode->edittree->nodes.first; node; node = node->next) {
1366                 if (node->flag & SELECT) {
1367
1368                         if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0)
1369                                 continue;
1370                         if (toggle_flag == NODE_OPTIONS && !(node->typeinfo->draw_buttons || node->typeinfo->draw_buttons_ex))
1371                                 continue;
1372
1373                         if (node->flag & toggle_flag)
1374                                 tot_eq++;
1375                         else
1376                                 tot_neq++;
1377                 }
1378         }
1379         for (node = snode->edittree->nodes.first; node; node = node->next) {
1380                 if (node->flag & SELECT) {
1381
1382                         if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0)
1383                                 continue;
1384                         if (toggle_flag == NODE_OPTIONS && !(node->typeinfo->draw_buttons || node->typeinfo->draw_buttons_ex))
1385                                 continue;
1386
1387                         if ((tot_eq && tot_neq) || tot_eq == 0)
1388                                 node->flag |= toggle_flag;
1389                         else
1390                                 node->flag &= ~toggle_flag;
1391                 }
1392         }
1393 }
1394
1395 static int node_hide_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1396 {
1397         SpaceNode *snode = CTX_wm_space_node(C);
1398
1399         /* sanity checking (poll callback checks this already) */
1400         if ((snode == NULL) || (snode->edittree == NULL))
1401                 return OPERATOR_CANCELLED;
1402
1403         node_flag_toggle_exec(snode, NODE_HIDDEN);
1404
1405         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1406
1407         return OPERATOR_FINISHED;
1408 }
1409
1410 void NODE_OT_hide_toggle(wmOperatorType *ot)
1411 {
1412         /* identifiers */
1413         ot->name = "Hide";
1414         ot->description = "Toggle hiding of selected nodes";
1415         ot->idname = "NODE_OT_hide_toggle";
1416
1417         /* callbacks */
1418         ot->exec = node_hide_toggle_exec;
1419         ot->poll = ED_operator_node_active;
1420
1421         /* flags */
1422         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1423 }
1424
1425 static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1426 {
1427         SpaceNode *snode = CTX_wm_space_node(C);
1428
1429         /* sanity checking (poll callback checks this already) */
1430         if ((snode == NULL) || (snode->edittree == NULL))
1431                 return OPERATOR_CANCELLED;
1432
1433         ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
1434
1435         node_flag_toggle_exec(snode, NODE_PREVIEW);
1436
1437         snode_notify(C, snode);
1438
1439         return OPERATOR_FINISHED;
1440 }
1441
1442 void NODE_OT_preview_toggle(wmOperatorType *ot)
1443 {
1444         /* identifiers */
1445         ot->name = "Toggle Node Preview";
1446         ot->description = "Toggle preview display for selected nodes";
1447         ot->idname = "NODE_OT_preview_toggle";
1448
1449         /* callbacks */
1450         ot->exec = node_preview_toggle_exec;
1451         ot->poll = ED_operator_node_active;
1452
1453         /* flags */
1454         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1455 }
1456
1457 static int node_options_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1458 {
1459         SpaceNode *snode = CTX_wm_space_node(C);
1460
1461         /* sanity checking (poll callback checks this already) */
1462         if ((snode == NULL) || (snode->edittree == NULL))
1463                 return OPERATOR_CANCELLED;
1464
1465         node_flag_toggle_exec(snode, NODE_OPTIONS);
1466
1467         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1468
1469         return OPERATOR_FINISHED;
1470 }
1471
1472 void NODE_OT_options_toggle(wmOperatorType *ot)
1473 {
1474         /* identifiers */
1475         ot->name = "Toggle Node Options";
1476         ot->description = "Toggle option buttons display for selected nodes";
1477         ot->idname = "NODE_OT_options_toggle";
1478
1479         /* callbacks */
1480         ot->exec = node_options_toggle_exec;
1481         ot->poll = ED_operator_node_active;
1482
1483         /* flags */
1484         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1485 }
1486
1487 static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
1488 {
1489         SpaceNode *snode = CTX_wm_space_node(C);
1490         bNode *node;
1491         int hidden;
1492
1493         /* sanity checking (poll callback checks this already) */
1494         if ((snode == NULL) || (snode->edittree == NULL))
1495                 return OPERATOR_CANCELLED;
1496
1497         ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
1498
1499         /* Toggle for all selected nodes */
1500         hidden = 0;
1501         for (node = snode->edittree->nodes.first; node; node = node->next) {
1502                 if (node->flag & SELECT) {
1503                         if (node_has_hidden_sockets(node)) {
1504                                 hidden = 1;
1505                                 break;
1506                         }
1507                 }
1508         }
1509
1510         for (node = snode->edittree->nodes.first; node; node = node->next) {
1511                 if (node->flag & SELECT) {
1512                         node_set_hidden_sockets(snode, node, !hidden);
1513                 }
1514         }
1515
1516         ntreeUpdateTree(CTX_data_main(C), snode->edittree);
1517
1518         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1519
1520         return OPERATOR_FINISHED;
1521 }
1522
1523 void NODE_OT_hide_socket_toggle(wmOperatorType *ot)
1524 {
1525         /* identifiers */
1526         ot->name = "Toggle Hidden Node Sockets";
1527         ot->description = "Toggle unused node socket display";
1528         ot->idname = "NODE_OT_hide_socket_toggle";
1529
1530         /* callbacks */
1531         ot->exec = node_socket_toggle_exec;
1532         ot->poll = ED_operator_node_active;
1533
1534         /* flags */
1535         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1536 }
1537
1538 /* ****************** Mute operator *********************** */
1539
1540 static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
1541 {
1542         Main *bmain = CTX_data_main(C);
1543         SpaceNode *snode = CTX_wm_space_node(C);
1544         bNode *node;
1545         bool do_tag_update = false;
1546
1547         ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
1548
1549         for (node = snode->edittree->nodes.first; node; node = node->next) {
1550                 /* Only allow muting of nodes having a mute func! */
1551                 if ((node->flag & SELECT) && node->typeinfo->update_internal_links) {
1552                         node->flag ^= NODE_MUTED;
1553                         snode_update(snode, node);
1554                         do_tag_update |= (do_tag_update || node_connected_to_output(bmain, snode->edittree, node));
1555                 }
1556         }
1557
1558         snode_notify(C, snode);
1559         if (do_tag_update) {
1560                 snode_dag_update(C, snode);
1561         }
1562
1563         return OPERATOR_FINISHED;
1564 }
1565
1566 void NODE_OT_mute_toggle(wmOperatorType *ot)
1567 {
1568         /* identifiers */
1569         ot->name = "Toggle Node Mute";
1570         ot->description = "Toggle muting of the nodes";
1571         ot->idname = "NODE_OT_mute_toggle";
1572
1573         /* callbacks */
1574         ot->exec = node_mute_exec;
1575         ot->poll = ED_operator_node_editable;
1576
1577         /* flags */
1578         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1579 }
1580
1581 /* ****************** Delete operator ******************* */
1582
1583 static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
1584 {
1585         Main *bmain = CTX_data_main(C);
1586         SpaceNode *snode = CTX_wm_space_node(C);
1587         bNode *node, *next;
1588         bool do_tag_update = false;
1589
1590         ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
1591
1592         for (node = snode->edittree->nodes.first; node; node = next) {
1593                 next = node->next;
1594                 if (node->flag & SELECT) {
1595                         do_tag_update |= (do_tag_update || node_connected_to_output(bmain, snode->edittree, node));
1596                         nodeRemoveNode(bmain, snode->edittree, node, true);
1597                 }
1598         }
1599
1600         ntreeUpdateTree(CTX_data_main(C), snode->edittree);
1601
1602         snode_notify(C, snode);
1603         if (do_tag_update) {
1604                 snode_dag_update(C, snode);
1605         }
1606
1607         return OPERATOR_FINISHED;
1608 }
1609
1610 void NODE_OT_delete(wmOperatorType *ot)
1611 {
1612         /* identifiers */
1613         ot->name = "Delete";
1614         ot->description = "Delete selected nodes";
1615         ot->idname = "NODE_OT_delete";
1616
1617         /* api callbacks */
1618         ot->exec = node_delete_exec;
1619         ot->poll = ED_operator_node_editable;
1620
1621         /* flags */
1622         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1623 }
1624
1625 /* ****************** Switch View ******************* */
1626
1627 static bool node_switch_view_poll(bContext *C)
1628 {
1629         SpaceNode *snode = CTX_wm_space_node(C);
1630
1631         if (snode && snode->edittree)
1632                 return true;
1633
1634         return false;
1635 }
1636
1637 static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op))
1638 {
1639         SpaceNode *snode = CTX_wm_space_node(C);
1640         bNode *node, *next;
1641
1642         for (node = snode->edittree->nodes.first; node; node = next) {
1643                 next = node->next;
1644                 if (node->flag & SELECT) {
1645                         /* call the update function from the Switch View node */
1646                         node->update = NODE_UPDATE_OPERATOR;
1647                 }
1648         }
1649
1650         ntreeUpdateTree(CTX_data_main(C), snode->edittree);
1651
1652         snode_notify(C, snode);
1653         snode_dag_update(C, snode);
1654
1655         return OPERATOR_FINISHED;
1656 }
1657
1658 void NODE_OT_switch_view_update(wmOperatorType *ot)
1659 {
1660         /* identifiers */
1661         ot->name = "Update Views";
1662         ot->description = "Update views of selected node";
1663         ot->idname = "NODE_OT_switch_view_update";
1664
1665         /* api callbacks */
1666         ot->exec = node_switch_view_exec;
1667         ot->poll = node_switch_view_poll;
1668
1669         /* flags */
1670         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1671 }
1672
1673 /* ****************** Delete with reconnect ******************* */
1674 static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op))
1675 {
1676         Main *bmain = CTX_data_main(C);
1677         SpaceNode *snode = CTX_wm_space_node(C);
1678         bNode *node, *next;
1679
1680         ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
1681
1682         for (node = snode->edittree->nodes.first; node; node = next) {
1683                 next = node->next;
1684                 if (node->flag & SELECT) {
1685                         nodeInternalRelink(snode->edittree, node);
1686                         nodeRemoveNode(bmain, snode->edittree, node, true);
1687                 }
1688         }
1689
1690         ntreeUpdateTree(CTX_data_main(C), snode->edittree);
1691
1692         snode_notify(C, snode);
1693         snode_dag_update(C, snode);
1694
1695         return OPERATOR_FINISHED;
1696 }
1697
1698 void NODE_OT_delete_reconnect(wmOperatorType *ot)
1699 {
1700         /* identifiers */
1701         ot->name = "Delete with Reconnect";
1702         ot->description = "Delete nodes; will reconnect nodes as if deletion was muted";
1703         ot->idname = "NODE_OT_delete_reconnect";
1704
1705         /* api callbacks */
1706         ot->exec = node_delete_reconnect_exec;
1707         ot->poll = ED_operator_node_editable;
1708
1709         /* flags */
1710         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1711 }
1712
1713
1714 /* ****************** File Output Add Socket  ******************* */
1715
1716 static int node_output_file_add_socket_exec(bContext *C, wmOperator *op)
1717 {
1718         Scene *scene = CTX_data_scene(C);
1719         SpaceNode *snode = CTX_wm_space_node(C);
1720         PointerRNA ptr = CTX_data_pointer_get(C, "node");
1721         bNodeTree *ntree = NULL;
1722         bNode *node = NULL;
1723         char file_path[MAX_NAME];
1724
1725         if (ptr.data) {
1726                 node = ptr.data;
1727                 ntree = ptr.id.data;
1728         }
1729         else if (snode && snode->edittree) {
1730                 ntree = snode->edittree;
1731                 node = nodeGetActive(snode->edittree);
1732         }
1733
1734         if (!node || node->type != CMP_NODE_OUTPUT_FILE)
1735                 return OPERATOR_CANCELLED;
1736
1737         RNA_string_get(op->ptr, "file_path", file_path);
1738         ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format);
1739
1740         snode_notify(C, snode);
1741
1742         return OPERATOR_FINISHED;
1743 }
1744
1745 void NODE_OT_output_file_add_socket(wmOperatorType *ot)
1746 {
1747         /* identifiers */
1748         ot->name = "Add File Node Socket";
1749         ot->description = "Add a new input to a file output node";
1750         ot->idname = "NODE_OT_output_file_add_socket";
1751
1752         /* callbacks */
1753         ot->exec = node_output_file_add_socket_exec;
1754         ot->poll = composite_node_editable;
1755
1756         /* flags */
1757         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1758
1759         RNA_def_string(ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Sub-path of the output file");
1760 }
1761
1762 /* ****************** Multi File Output Remove Socket  ******************* */
1763
1764 static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op))
1765 {
1766         SpaceNode *snode = CTX_wm_space_node(C);
1767         PointerRNA ptr = CTX_data_pointer_get(C, "node");
1768         bNodeTree *ntree = NULL;
1769         bNode *node = NULL;
1770
1771         if (ptr.data) {
1772                 node = ptr.data;
1773                 ntree = ptr.id.data;
1774         }
1775         else if (snode && snode->edittree) {
1776                 ntree = snode->edittree;
1777                 node = nodeGetActive(snode->edittree);
1778         }
1779
1780         if (!node || node->type != CMP_NODE_OUTPUT_FILE)
1781                 return OPERATOR_CANCELLED;
1782
1783         if (!ntreeCompositOutputFileRemoveActiveSocket(ntree, node))
1784                 return OPERATOR_CANCELLED;
1785
1786         snode_notify(C, snode);
1787
1788         return OPERATOR_FINISHED;
1789 }
1790
1791 void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot)
1792 {
1793         /* identifiers */
1794         ot->name = "Remove File Node Socket";
1795         ot->description = "Remove active input from a file output node";
1796         ot->idname = "NODE_OT_output_file_remove_active_socket";
1797
1798         /* callbacks */
1799         ot->exec = node_output_file_remove_active_socket_exec;
1800         ot->poll = composite_node_editable;
1801
1802         /* flags */
1803         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1804 }
1805
1806 /* ****************** Multi File Output Move Socket  ******************* */
1807
1808 static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
1809 {
1810         SpaceNode *snode = CTX_wm_space_node(C);
1811         PointerRNA ptr = CTX_data_pointer_get(C, "node");
1812         bNode *node = NULL;
1813         NodeImageMultiFile *nimf;
1814         bNodeSocket *sock;
1815         int direction;
1816
1817         if (ptr.data)
1818                 node = ptr.data;
1819         else if (snode && snode->edittree)
1820                 node = nodeGetActive(snode->edittree);
1821
1822         if (!node || node->type != CMP_NODE_OUTPUT_FILE)
1823                 return OPERATOR_CANCELLED;
1824
1825         nimf = node->storage;
1826
1827         sock = BLI_findlink(&node->inputs, nimf->active_input);
1828         if (!sock)
1829                 return OPERATOR_CANCELLED;
1830
1831         direction = RNA_enum_get(op->ptr, "direction");
1832
1833         if (direction == 1) {
1834                 bNodeSocket *before = sock->prev;
1835                 if (!before)
1836                         return OPERATOR_CANCELLED;
1837                 BLI_remlink(&node->inputs, sock);
1838                 BLI_insertlinkbefore(&node->inputs, before, sock);
1839                 nimf->active_input--;
1840         }
1841         else {
1842                 bNodeSocket *after = sock->next;
1843                 if (!after)
1844                         return OPERATOR_CANCELLED;
1845                 BLI_remlink(&node->inputs, sock);
1846                 BLI_insertlinkafter(&node->inputs, after, sock);
1847                 nimf->active_input++;
1848         }
1849
1850         snode_notify(C, snode);
1851
1852         return OPERATOR_FINISHED;
1853 }
1854
1855 void NODE_OT_output_file_move_active_socket(wmOperatorType *ot)
1856 {
1857         static const EnumPropertyItem direction_items[] = {
1858                 {1, "UP", 0, "Up", ""},
1859                 {2, "DOWN", 0, "Down", ""},
1860                 { 0, NULL, 0, NULL, NULL }
1861         };
1862
1863         /* identifiers */
1864         ot->name = "Move File Node Socket";
1865         ot->description = "Move the active input of a file output node up or down the list";
1866         ot->idname = "NODE_OT_output_file_move_active_socket";
1867
1868         /* callbacks */
1869         ot->exec = node_output_file_move_active_socket_exec;
1870         ot->poll = composite_node_editable;
1871
1872         /* flags */
1873         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1874
1875         RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", "");
1876 }
1877
1878 /* ****************** Copy Node Color ******************* */
1879
1880 static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
1881 {
1882         SpaceNode *snode = CTX_wm_space_node(C);
1883         bNodeTree *ntree = snode->edittree;
1884         bNode *node, *tnode;
1885
1886         if (!ntree)
1887                 return OPERATOR_CANCELLED;
1888         node = nodeGetActive(ntree);
1889         if (!node)
1890                 return OPERATOR_CANCELLED;
1891
1892         for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
1893                 if (tnode->flag & NODE_SELECT && tnode != node) {
1894                         if (node->flag & NODE_CUSTOM_COLOR) {
1895                                 tnode->flag |= NODE_CUSTOM_COLOR;
1896                                 copy_v3_v3(tnode->color, node->color);
1897                         }
1898                         else
1899                                 tnode->flag &= ~NODE_CUSTOM_COLOR;
1900                 }
1901         }
1902
1903         ED_node_sort(ntree);
1904         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1905
1906         return OPERATOR_FINISHED;
1907 }
1908
1909 void NODE_OT_node_copy_color(wmOperatorType *ot)
1910 {
1911         /* identifiers */
1912         ot->name = "Copy Color";
1913         ot->description = "Copy color to all selected nodes";
1914         ot->idname = "NODE_OT_node_copy_color";
1915
1916         /* api callbacks */
1917         ot->exec = node_copy_color_exec;
1918         ot->poll = ED_operator_node_editable;
1919
1920         /* flags */
1921         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1922 }
1923
1924 /* ****************** Copy to clipboard ******************* */
1925
1926 static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
1927 {
1928         SpaceNode *snode = CTX_wm_space_node(C);
1929         bNodeTree *ntree = snode->edittree;
1930         bNode *node;
1931         bNodeLink *link, *newlink;
1932
1933         ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
1934
1935         /* clear current clipboard */
1936         BKE_node_clipboard_clear();
1937         BKE_node_clipboard_init(ntree);
1938
1939         for (node = ntree->nodes.first; node; node = node->next) {
1940                 if (node->flag & SELECT) {
1941                         /* No ID refcounting, this node is virtual, detached from any actual Blender data currently. */
1942                         bNode *new_node = BKE_node_copy_ex(NULL, node, LIB_ID_CREATE_NO_USER_REFCOUNT);
1943                         BKE_node_clipboard_add_node(new_node);
1944                 }
1945         }
1946
1947         for (node = ntree->nodes.first; node; node = node->next) {
1948                 if (node->flag & SELECT) {
1949                         bNode *new_node = node->new_node;
1950
1951                         /* ensure valid pointers */
1952                         if (new_node->parent) {
1953                                 /* parent pointer must be redirected to new node or detached if parent is
1954                                  * not copied */
1955                                 if (new_node->parent->flag & NODE_SELECT) {
1956                                         new_node->parent = new_node->parent->new_node;
1957                                 }
1958                                 else {
1959                                         nodeDetachNode(new_node);
1960                                 }
1961                         }
1962                 }
1963         }
1964
1965         /* copy links between selected nodes
1966          * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy!
1967          */
1968         for (link = ntree->links.first; link; link = link->next) {
1969                 /* This creates new links between copied nodes. */
1970                 if (link->tonode && (link->tonode->flag & NODE_SELECT) &&
1971                     link->fromnode && (link->fromnode->flag & NODE_SELECT))
1972                 {
1973                         newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
1974                         newlink->flag = link->flag;
1975                         newlink->tonode = link->tonode->new_node;
1976                         newlink->tosock = link->tosock->new_sock;
1977                         newlink->fromnode = link->fromnode->new_node;
1978                         newlink->fromsock = link->fromsock->new_sock;
1979
1980                         BKE_node_clipboard_add_link(newlink);
1981                 }
1982         }
1983
1984         return OPERATOR_FINISHED;
1985 }
1986
1987 void NODE_OT_clipboard_copy(wmOperatorType *ot)
1988 {
1989         /* identifiers */
1990         ot->name = "Copy to Clipboard";
1991         ot->description = "Copies selected nodes to the clipboard";
1992         ot->idname = "NODE_OT_clipboard_copy";
1993
1994         /* api callbacks */
1995         ot->exec = node_clipboard_copy_exec;
1996         ot->poll = ED_operator_node_active;
1997
1998         /* flags */
1999         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2000 }
2001
2002 /* ****************** Paste from clipboard ******************* */
2003
2004 static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
2005 {
2006         SpaceNode *snode = CTX_wm_space_node(C);
2007         bNodeTree *ntree = snode->edittree;
2008         const ListBase *clipboard_nodes_lb;
2009         const ListBase *clipboard_links_lb;
2010         bNode *node;
2011         bNodeLink *link;
2012         int num_nodes;
2013         float center[2];
2014         bool is_clipboard_valid, all_nodes_valid;
2015
2016         /* validate pointers in the clipboard */
2017         is_clipboard_valid = BKE_node_clipboard_validate();
2018         clipboard_nodes_lb = BKE_node_clipboard_get_nodes();
2019         clipboard_links_lb = BKE_node_clipboard_get_links();
2020
2021         if (BLI_listbase_is_empty(clipboard_nodes_lb)) {
2022                 BKE_report(op->reports, RPT_ERROR, "Clipboard is empty");
2023                 return OPERATOR_CANCELLED;
2024         }
2025
2026         if (BKE_node_clipboard_get_type() != ntree->type) {
2027                 BKE_report(op->reports, RPT_ERROR, "Clipboard nodes are an incompatible type");
2028                 return OPERATOR_CANCELLED;
2029         }
2030
2031         /* only warn */
2032         if (is_clipboard_valid == false) {
2033                 BKE_report(op->reports, RPT_WARNING, "Some nodes references could not be restored, will be left empty");
2034         }
2035
2036         /* make sure all clipboard nodes would be valid in the target tree */
2037         all_nodes_valid = true;
2038         for (node = clipboard_nodes_lb->first; node; node = node->next) {
2039                 if (!node->typeinfo->poll_instance || !node->typeinfo->poll_instance(node, ntree)) {
2040                         all_nodes_valid = false;
2041                         BKE_reportf(op->reports, RPT_ERROR, "Cannot add node %s into node tree %s", node->name, ntree->id.name + 2);
2042                 }
2043         }
2044         if (!all_nodes_valid)
2045                 return OPERATOR_CANCELLED;
2046
2047         ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
2048
2049         /* deselect old nodes */
2050         node_deselect_all(snode);
2051
2052         /* calculate "barycenter" for placing on mouse cursor */
2053         zero_v2(center);
2054         for (node = clipboard_nodes_lb->first, num_nodes = 0; node; node = node->next, num_nodes++) {
2055                 center[0] += BLI_rctf_cent_x(&node->totr);
2056                 center[1] += BLI_rctf_cent_y(&node->totr);
2057         }
2058         mul_v2_fl(center, 1.0 / num_nodes);
2059
2060         /* copy nodes from clipboard */
2061         for (node = clipboard_nodes_lb->first; node; node = node->next) {
2062                 bNode *new_node = BKE_node_copy_ex(ntree, node, LIB_ID_COPY_DEFAULT);
2063
2064                 /* pasted nodes are selected */
2065                 nodeSetSelected(new_node, true);
2066         }
2067
2068         /* reparent copied nodes */
2069         for (node = clipboard_nodes_lb->first; node; node = node->next) {
2070                 bNode *new_node = node->new_node;
2071                 if (new_node->parent)
2072                         new_node->parent = new_node->parent->new_node;
2073         }
2074
2075         for (link = clipboard_links_lb->first; link; link = link->next) {
2076                 nodeAddLink(ntree, link->fromnode->new_node, link->fromsock->new_sock,
2077                             link->tonode->new_node, link->tosock->new_sock);
2078         }
2079
2080         ntreeUpdateTree(CTX_data_main(C), snode->edittree);
2081
2082         snode_notify(C, snode);
2083         snode_dag_update(C, snode);
2084
2085         return OPERATOR_FINISHED;
2086 }
2087
2088 void NODE_OT_clipboard_paste(wmOperatorType *ot)
2089 {
2090         /* identifiers */
2091         ot->name = "Paste from Clipboard";
2092         ot->description = "Pastes nodes from the clipboard to the active node tree";
2093         ot->idname = "NODE_OT_clipboard_paste";
2094
2095         /* api callbacks */
2096         ot->exec = node_clipboard_paste_exec;
2097         ot->poll = ED_operator_node_editable;
2098
2099         /* flags */
2100         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2101 }
2102
2103 /********************** Add interface socket operator *********************/
2104
2105 static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb)
2106 {
2107         bNodeSocket *sock;
2108         for (sock = lb->first; sock; sock = sock->next)
2109                 if (sock->flag & SELECT)
2110                         return sock;
2111         return NULL;
2112 }
2113
2114 static int ntree_socket_add_exec(bContext *C, wmOperator *op)
2115 {
2116         SpaceNode *snode = CTX_wm_space_node(C);
2117         bNodeTree *ntree = snode->edittree;
2118         int in_out = RNA_enum_get(op->ptr, "in_out");
2119         PointerRNA ntree_ptr;
2120         bNodeSocket *sock, *tsock, *active_sock;
2121         const char *default_name;
2122
2123         RNA_id_pointer_create((ID *)ntree, &ntree_ptr);
2124
2125         if (in_out == SOCK_IN) {
2126                 active_sock = ntree_get_active_interface_socket(&ntree->inputs);
2127                 default_name = "Input";
2128         }
2129         else {
2130                 active_sock = ntree_get_active_interface_socket(&ntree->outputs);
2131                 default_name = "Output";
2132         }
2133
2134         if (active_sock) {
2135                 /* insert a copy of the active socket right after it */
2136                 sock = ntreeInsertSocketInterface(ntree, in_out, active_sock->idname, active_sock->next, active_sock->name);
2137                 /* XXX this only works for actual sockets, not interface templates! */
2138                 /*nodeSocketCopyValue(sock, &ntree_ptr, active_sock, &ntree_ptr);*/
2139         }
2140         else {
2141                 /* XXX TODO define default socket type for a tree! */
2142                 sock = ntreeAddSocketInterface(ntree, in_out, "NodeSocketFloat", default_name);
2143         }
2144
2145         /* deactivate sockets (has to check both lists) */
2146         for (tsock = ntree->inputs.first; tsock; tsock = tsock->next)
2147                 tsock->flag &= ~SELECT;
2148         for (tsock = ntree->outputs.first; tsock; tsock = tsock->next)
2149                 tsock->flag &= ~SELECT;
2150         /* make the new socket active */
2151         sock->flag |= SELECT;
2152
2153         ntreeUpdateTree(CTX_data_main(C), ntree);
2154
2155         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
2156
2157         return OPERATOR_FINISHED;
2158 }
2159
2160 void NODE_OT_tree_socket_add(wmOperatorType *ot)
2161 {
2162         /* identifiers */
2163         ot->name = "Add Node Tree Interface Socket";
2164         ot->description = "Add an input or output socket to the current node tree";
2165         ot->idname = "NODE_OT_tree_socket_add";
2166
2167         /* api callbacks */
2168         ot->exec = ntree_socket_add_exec;
2169         ot->poll = ED_operator_node_editable;
2170
2171         /* flags */
2172         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2173
2174         RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", "");
2175 }
2176
2177 /********************** Remove interface socket operator *********************/
2178
2179 static int ntree_socket_remove_exec(bContext *C, wmOperator *UNUSED(op))
2180 {
2181         SpaceNode *snode = CTX_wm_space_node(C);
2182         bNodeTree *ntree = snode->edittree;
2183         bNodeSocket *iosock, *active_sock;
2184
2185         iosock = ntree_get_active_interface_socket(&ntree->inputs);
2186         if (!iosock)
2187                 iosock = ntree_get_active_interface_socket(&ntree->outputs);
2188         if (!iosock)
2189                 return OPERATOR_CANCELLED;
2190
2191         /* preferably next socket becomes active, otherwise try previous socket */
2192         active_sock = (iosock->next ? iosock->next : iosock->prev);
2193         ntreeRemoveSocketInterface(ntree, iosock);
2194
2195         /* set active socket */
2196         if (active_sock)
2197                 active_sock->flag |= SELECT;
2198
2199         ntreeUpdateTree(CTX_data_main(C), ntree);
2200
2201         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
2202
2203         return OPERATOR_FINISHED;
2204 }
2205
2206 void NODE_OT_tree_socket_remove(wmOperatorType *ot)
2207 {
2208         /* identifiers */
2209         ot->name = "Remove Node Tree Interface Socket";
2210         ot->description = "Remove an input or output socket to the current node tree";
2211         ot->idname = "NODE_OT_tree_socket_remove";
2212
2213         /* api callbacks */
2214         ot->exec = ntree_socket_remove_exec;
2215         ot->poll = ED_operator_node_editable;
2216
2217         /* flags */
2218         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2219 }
2220
2221 /********************** Move interface socket operator *********************/
2222
2223 static const EnumPropertyItem move_direction_items[] = {
2224         { 1, "UP", 0, "Up", "" },
2225         { 2, "DOWN", 0, "Down", "" },
2226         { 0, NULL, 0, NULL, NULL },
2227 };
2228
2229 static int ntree_socket_move_exec(bContext *C, wmOperator *op)
2230 {
2231         SpaceNode *snode = CTX_wm_space_node(C);
2232         bNodeTree *ntree = snode->edittree;
2233         int direction = RNA_enum_get(op->ptr, "direction");
2234         bNodeSocket *iosock;
2235         ListBase *lb;
2236
2237         lb = &ntree->inputs;
2238         iosock = ntree_get_active_interface_socket(lb);
2239         if (!iosock) {
2240                 lb = &ntree->outputs;
2241                 iosock = ntree_get_active_interface_socket(lb);
2242         }
2243         if (!iosock)
2244                 return OPERATOR_CANCELLED;
2245
2246         switch (direction) {
2247                 case 1:
2248                 {       /* up */
2249                         bNodeSocket *before = iosock->prev;
2250                         BLI_remlink(lb, iosock);
2251                         if (before)
2252                                 BLI_insertlinkbefore(lb, before, iosock);
2253                         else
2254                                 BLI_addhead(lb, iosock);
2255                         break;
2256                 }
2257                 case 2:
2258                 {       /* down */
2259                         bNodeSocket *after = iosock->next;
2260                         BLI_remlink(lb, iosock);
2261                         if (after)
2262                                 BLI_insertlinkafter(lb, after, iosock);
2263                         else
2264                                 BLI_addtail(lb, iosock);
2265                         break;
2266                 }
2267         }
2268
2269         ntreeUpdateTree(CTX_data_main(C), ntree);
2270
2271         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
2272
2273         return OPERATOR_FINISHED;
2274 }
2275
2276 void NODE_OT_tree_socket_move(wmOperatorType *ot)
2277 {
2278         /* identifiers */
2279         ot->name = "Move Node Tree Socket";
2280         ot->description = "Move a socket up or down in the current node tree's sockets stack";
2281         ot->idname = "NODE_OT_tree_socket_move";
2282
2283         /* api callbacks */
2284         ot->exec = ntree_socket_move_exec;
2285         ot->poll = ED_operator_node_editable;
2286
2287         /* flags */
2288         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2289
2290         RNA_def_enum(ot->srna, "direction", move_direction_items, 1, "Direction", "");
2291 }
2292
2293 /* ********************** Shader Script Update ******************/
2294
2295 static bool node_shader_script_update_poll(bContext *C)
2296 {
2297         Scene *scene = CTX_data_scene(C);
2298         RenderEngineType *type = RE_engines_find(scene->r.engine);
2299         SpaceNode *snode = CTX_wm_space_node(C);
2300         bNode *node;
2301         Text *text;
2302
2303         /* test if we have a render engine that supports shaders scripts */
2304         if (!(type && type->update_script_node))
2305                 return 0;
2306
2307         /* see if we have a shader script node in context */
2308         node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data;
2309
2310         if (!node && snode && snode->edittree)
2311                 node = nodeGetActive(snode->edittree);
2312
2313         if (node && node->type == SH_NODE_SCRIPT) {
2314                 NodeShaderScript *nss = node->storage;
2315
2316                 if (node->id || nss->filepath[0]) {
2317                         return ED_operator_node_editable(C);
2318                 }
2319         }
2320
2321         /* see if we have a text datablock in context */
2322         text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
2323         if (text)
2324                 return 1;
2325
2326         /* we don't check if text datablock is actually in use, too slow for poll */
2327
2328         return 0;
2329 }
2330
2331 /* recursively check for script nodes in groups using this text and update */
2332 static bool node_shader_script_update_text_recursive(RenderEngine *engine, RenderEngineType *type, bNodeTree *ntree, Text *text)
2333 {
2334         bool found = false;
2335         bNode *node;
2336
2337         ntree->done = true;
2338
2339         /* update each script that is using this text datablock */
2340         for (node = ntree->nodes.first; node; node = node->next) {
2341                 if (node->type == NODE_GROUP) {
2342                         bNodeTree *ngroup = (bNodeTree *)node->id;
2343                         if (ngroup && !ngroup->done)
2344                                 found |= node_shader_script_update_text_recursive(engine, type, ngroup, text);
2345                 }
2346                 else if (node->type == SH_NODE_SCRIPT && node->id == &text->id) {
2347                         type->update_script_node(engine, ntree, node);
2348                         found = true;
2349                 }
2350         }
2351
2352         return found;
2353 }
2354
2355 static int node_shader_script_update_exec(bContext *C, wmOperator *op)
2356 {
2357         Main *bmain = CTX_data_main(C);
2358         Scene *scene = CTX_data_scene(C);
2359         SpaceNode *snode = CTX_wm_space_node(C);
2360         PointerRNA nodeptr = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript);
2361         bNodeTree *ntree_base = NULL;
2362         bNode *node = NULL;
2363         RenderEngine *engine;
2364         RenderEngineType *type;
2365         bool found = false;
2366
2367         /* setup render engine */
2368         type = RE_engines_find(scene->r.engine);
2369         engine = RE_engine_create(type);
2370         engine->reports = op->reports;
2371
2372         /* get node */
2373         if (nodeptr.data) {
2374                 ntree_base = nodeptr.id.data;
2375                 node = nodeptr.data;
2376         }
2377         else if (snode && snode->edittree) {
2378                 ntree_base = snode->edittree;
2379                 node = nodeGetActive(snode->edittree);
2380         }
2381
2382         if (node) {
2383                 /* update single node */
2384                 type->update_script_node(engine, ntree_base, node);
2385
2386                 found = true;
2387         }
2388         else {
2389                 /* update all nodes using text datablock */
2390                 Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
2391
2392                 if (text) {
2393                         /* clear flags for recursion check */
2394                         FOREACH_NODETREE_BEGIN(bmain, ntree, id) {
2395                                 if (ntree->type == NTREE_SHADER)
2396                                         ntree->done = false;
2397                         } FOREACH_NODETREE_END;
2398
2399                         FOREACH_NODETREE_BEGIN(bmain, ntree, id) {
2400                                 if (ntree->type == NTREE_SHADER) {
2401                                         if (!ntree->done)
2402                                                 found |= node_shader_script_update_text_recursive(engine, type, ntree, text);
2403                                 }
2404                         } FOREACH_NODETREE_END;
2405
2406                         if (!found)
2407                                 BKE_report(op->reports, RPT_INFO, "Text not used by any node, no update done");
2408                 }
2409         }
2410
2411         RE_engine_free(engine);
2412
2413         return (found) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2414 }
2415
2416 void NODE_OT_shader_script_update(wmOperatorType *ot)
2417 {
2418         /* identifiers */
2419         ot->name = "Script Node Update";
2420         ot->description = "Update shader script node with new sockets and options from the script";
2421         ot->idname = "NODE_OT_shader_script_update";
2422
2423         /* api callbacks */
2424         ot->exec = node_shader_script_update_exec;
2425         ot->poll = node_shader_script_update_poll;
2426
2427         /* flags */
2428         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2429 }
2430
2431 /* ********************** Viewer border ******************/
2432
2433 static void viewer_border_corner_to_backdrop(SpaceNode *snode, ARegion *ar, int x, int y,
2434                                              int backdrop_width, int backdrop_height,
2435                                              float *fx, float *fy)
2436 {
2437         float bufx, bufy;
2438
2439         bufx = backdrop_width * snode->zoom;
2440         bufy = backdrop_height * snode->zoom;
2441
2442         *fx = (bufx > 0.0f ? ((float) x - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f);
2443         *fy = (bufy > 0.0f ? ((float) y - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f);
2444 }
2445
2446 static int viewer_border_exec(bContext *C, wmOperator *op)
2447 {
2448         Main *bmain = CTX_data_main(C);
2449         Image *ima;
2450         void *lock;
2451         ImBuf *ibuf;
2452
2453         ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
2454
2455         ima = BKE_image_verify_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
2456         ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
2457
2458         if (ibuf) {
2459                 ARegion *ar = CTX_wm_region(C);
2460                 SpaceNode *snode = CTX_wm_space_node(C);
2461                 bNodeTree *btree = snode->nodetree;
2462                 rcti rect;
2463                 rctf rectf;
2464
2465                 /* get border from operator */
2466                 WM_operator_properties_border_to_rcti(op, &rect);
2467
2468                 /* convert border to unified space within backdrop image */
2469                 viewer_border_corner_to_backdrop(snode, ar, rect.xmin, rect.ymin, ibuf->x, ibuf->y,
2470                                                  &rectf.xmin, &rectf.ymin);
2471
2472                 viewer_border_corner_to_backdrop(snode, ar, rect.xmax, rect.ymax, ibuf->x, ibuf->y,
2473                                                  &rectf.xmax, &rectf.ymax);
2474
2475                 /* clamp coordinates */
2476                 rectf.xmin = max_ff(rectf.xmin, 0.0f);
2477                 rectf.ymin = max_ff(rectf.ymin, 0.0f);
2478                 rectf.xmax = min_ff(rectf.xmax, 1.0f);
2479                 rectf.ymax = min_ff(rectf.ymax, 1.0f);
2480
2481                 if (rectf.xmin < rectf.xmax && rectf.ymin < rectf.ymax) {
2482                         btree->viewer_border = rectf;
2483
2484                         if (rectf.xmin == 0.0f && rectf.ymin == 0.0f &&
2485                             rectf.xmax == 1.0f && rectf.ymax == 1.0f)
2486                         {
2487                                 btree->flag &= ~NTREE_VIEWER_BORDER;
2488                         }
2489                         else {
2490                                 btree->flag |= NTREE_VIEWER_BORDER;
2491                         }
2492
2493                         snode_notify(C, snode);
2494                         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
2495                 }
2496                 else {
2497                         btree->flag &= ~NTREE_VIEWER_BORDER;
2498                 }
2499         }
2500
2501         BKE_image_release_ibuf(ima, ibuf, lock);
2502
2503         return OPERATOR_FINISHED;
2504 }
2505
2506 void NODE_OT_viewer_border(wmOperatorType *ot)
2507 {
2508         /* identifiers */
2509         ot->name = "Viewer Border";
2510         ot->description = "Set the boundaries for viewer operations";
2511         ot->idname = "NODE_OT_viewer_border";
2512
2513         /* api callbacks */
2514         ot->invoke = WM_gesture_box_invoke;
2515         ot->exec = viewer_border_exec;
2516         ot->modal = WM_gesture_box_modal;
2517         ot->cancel = WM_gesture_box_cancel;
2518         ot->poll = composite_node_active;
2519
2520         /* flags */
2521         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2522
2523         /* properties */
2524         WM_operator_properties_gesture_box_select(ot);
2525 }
2526
2527 static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op))
2528 {
2529         SpaceNode *snode = CTX_wm_space_node(C);
2530         bNodeTree *btree = snode->nodetree;
2531
2532         btree->flag &= ~NTREE_VIEWER_BORDER;
2533         snode_notify(C, snode);
2534         WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
2535
2536         return OPERATOR_FINISHED;
2537 }
2538
2539 void NODE_OT_clear_viewer_border(wmOperatorType *ot)
2540 {
2541         /* identifiers */
2542         ot->name = "Clear Viewer Border";
2543         ot->description = "Clear the boundaries for viewer operations";
2544         ot->idname = "NODE_OT_clear_viewer_border";
2545
2546         /* api callbacks */
2547         ot->exec = clear_viewer_border_exec;
2548         ot->poll = composite_node_active;
2549
2550         /* flags */
2551         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2552 }
2553
2554 /* ****************** Cryptomatte Add Socket  ******************* */
2555
2556 static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op))
2557 {
2558         SpaceNode *snode = CTX_wm_space_node(C);
2559         PointerRNA ptr = CTX_data_pointer_get(C, "node");
2560         bNodeTree *ntree = NULL;
2561         bNode *node = NULL;
2562
2563         if (ptr.data) {
2564                 node = ptr.data;
2565                 ntree = ptr.id.data;
2566         }
2567         else if (snode && snode->edittree) {
2568                 ntree = snode->edittree;
2569                 node = nodeGetActive(snode->edittree);
2570         }
2571
2572         if (!node || node->type != CMP_NODE_CRYPTOMATTE) {
2573                 return OPERATOR_CANCELLED;
2574         }
2575
2576         ntreeCompositCryptomatteAddSocket(ntree, node);
2577
2578         snode_notify(C, snode);
2579
2580         return OPERATOR_FINISHED;
2581 }
2582
2583 void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot)
2584 {
2585         /* identifiers */
2586         ot->name = "Add Cryptomatte Socket";
2587         ot->description = "Add a new input layer to a Cryptomatte node";
2588         ot->idname = "NODE_OT_cryptomatte_layer_add";
2589
2590         /* callbacks */
2591         ot->exec = node_cryptomatte_add_socket_exec;
2592         ot->poll = composite_node_editable;
2593
2594         /* flags */
2595         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2596 }
2597
2598 /* ****************** Cryptomatte Remove Socket  ******************* */
2599
2600 static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(op))
2601 {
2602         SpaceNode *snode = CTX_wm_space_node(C);
2603         PointerRNA ptr = CTX_data_pointer_get(C, "node");
2604         bNodeTree *ntree = NULL;
2605         bNode *node = NULL;
2606
2607         if (ptr.data) {
2608                 node = ptr.data;
2609                 ntree = ptr.id.data;
2610         }
2611         else if (snode && snode->edittree) {
2612                 ntree = snode->edittree;
2613                 node = nodeGetActive(snode->edittree);
2614         }
2615
2616         if (!node || node->type != CMP_NODE_CRYPTOMATTE) {
2617                 return OPERATOR_CANCELLED;
2618         }
2619
2620         if (!ntreeCompositCryptomatteRemoveSocket(ntree, node)) {
2621                 return OPERATOR_CANCELLED;
2622         }
2623
2624         snode_notify(C, snode);
2625
2626         return OPERATOR_FINISHED;
2627 }
2628
2629 void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot)
2630 {
2631         /* identifiers */
2632         ot->name = "Remove Cryptomatte Socket";
2633         ot->description = "Remove layer from a Cryptomatte node";
2634         ot->idname = "NODE_OT_cryptomatte_layer_remove";
2635
2636         /* callbacks */
2637         ot->exec = node_cryptomatte_remove_socket_exec;
2638         ot->poll = composite_node_editable;
2639
2640         /* flags */
2641         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2642 }