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