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