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